agora inbox for [email protected]help / color / mirror / Atom feed
[PATCH 8/8] reverse arguments .. isn't the other order a bug? 94+ messages / 3 participants [nested] [flat]
* [PATCH 8/8] reverse arguments .. isn't the other order a bug? @ 2020-03-12 21:33 Alvaro Herrera <[email protected]> 0 siblings, 0 replies; 94+ 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] 94+ messages in thread
* [PATCH v13 7/8] Row pattern recognition patch (tests). @ 2024-01-22 09:45 Tatsuo Ishii <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Tatsuo Ishii @ 2024-01-22 09:45 UTC (permalink / raw) --- src/test/regress/expected/rpr.out | 821 +++++++++++++++++++++++++++++ src/test/regress/parallel_schedule | 2 +- src/test/regress/sql/rpr.sql | 392 ++++++++++++++ 3 files changed, 1214 insertions(+), 1 deletion(-) create mode 100644 src/test/regress/expected/rpr.out create mode 100644 src/test/regress/sql/rpr.sql diff --git a/src/test/regress/expected/rpr.out b/src/test/regress/expected/rpr.out new file mode 100644 index 0000000000..e8998ebf45 --- /dev/null +++ b/src/test/regress/expected/rpr.out @@ -0,0 +1,821 @@ +-- +-- Test for row pattern definition clause +-- +CREATE TEMP TABLE stock ( + company TEXT, + tdate DATE, + price INTEGER +); +INSERT INTO stock VALUES ('company1', '2023-07-01', 100); +INSERT INTO stock VALUES ('company1', '2023-07-02', 200); +INSERT INTO stock VALUES ('company1', '2023-07-03', 150); +INSERT INTO stock VALUES ('company1', '2023-07-04', 140); +INSERT INTO stock VALUES ('company1', '2023-07-05', 150); +INSERT INTO stock VALUES ('company1', '2023-07-06', 90); +INSERT INTO stock VALUES ('company1', '2023-07-07', 110); +INSERT INTO stock VALUES ('company1', '2023-07-08', 130); +INSERT INTO stock VALUES ('company1', '2023-07-09', 120); +INSERT INTO stock VALUES ('company1', '2023-07-10', 130); +INSERT INTO stock VALUES ('company2', '2023-07-01', 50); +INSERT INTO stock VALUES ('company2', '2023-07-02', 2000); +INSERT INTO stock VALUES ('company2', '2023-07-03', 1500); +INSERT INTO stock VALUES ('company2', '2023-07-04', 1400); +INSERT INTO stock VALUES ('company2', '2023-07-05', 1500); +INSERT INTO stock VALUES ('company2', '2023-07-06', 60); +INSERT INTO stock VALUES ('company2', '2023-07-07', 1100); +INSERT INTO stock VALUES ('company2', '2023-07-08', 1300); +INSERT INTO stock VALUES ('company2', '2023-07-09', 1200); +INSERT INTO stock VALUES ('company2', '2023-07-10', 1300); +SELECT * FROM stock; + company | tdate | price +----------+------------+------- + company1 | 07-01-2023 | 100 + company1 | 07-02-2023 | 200 + company1 | 07-03-2023 | 150 + company1 | 07-04-2023 | 140 + company1 | 07-05-2023 | 150 + company1 | 07-06-2023 | 90 + company1 | 07-07-2023 | 110 + company1 | 07-08-2023 | 130 + company1 | 07-09-2023 | 120 + company1 | 07-10-2023 | 130 + company2 | 07-01-2023 | 50 + company2 | 07-02-2023 | 2000 + company2 | 07-03-2023 | 1500 + company2 | 07-04-2023 | 1400 + company2 | 07-05-2023 | 1500 + company2 | 07-06-2023 | 60 + company2 | 07-07-2023 | 1100 + company2 | 07-08-2023 | 1300 + company2 | 07-09-2023 | 1200 + company2 | 07-10-2023 | 1300 +(20 rows) + +-- basic test using PREV +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w, + nth_value(tdate, 2) OVER w AS nth_second + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UP+ DOWN+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + company | tdate | price | first_value | last_value | nth_second +----------+------------+-------+-------------+------------+------------ + company1 | 07-01-2023 | 100 | 100 | 140 | 07-02-2023 + company1 | 07-02-2023 | 200 | | | + company1 | 07-03-2023 | 150 | | | + company1 | 07-04-2023 | 140 | | | + company1 | 07-05-2023 | 150 | | | + company1 | 07-06-2023 | 90 | 90 | 120 | 07-07-2023 + company1 | 07-07-2023 | 110 | | | + company1 | 07-08-2023 | 130 | | | + company1 | 07-09-2023 | 120 | | | + company1 | 07-10-2023 | 130 | | | + company2 | 07-01-2023 | 50 | 50 | 1400 | 07-02-2023 + company2 | 07-02-2023 | 2000 | | | + company2 | 07-03-2023 | 1500 | | | + company2 | 07-04-2023 | 1400 | | | + company2 | 07-05-2023 | 1500 | | | + company2 | 07-06-2023 | 60 | 60 | 1200 | 07-07-2023 + company2 | 07-07-2023 | 1100 | | | + company2 | 07-08-2023 | 1300 | | | + company2 | 07-09-2023 | 1200 | | | + company2 | 07-10-2023 | 1300 | | | +(20 rows) + +-- basic test using PREV. UP appears twice +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w, + nth_value(tdate, 2) OVER w AS nth_second + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UP+ DOWN+ UP+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + company | tdate | price | first_value | last_value | nth_second +----------+------------+-------+-------------+------------+------------ + company1 | 07-01-2023 | 100 | 100 | 150 | 07-02-2023 + company1 | 07-02-2023 | 200 | | | + company1 | 07-03-2023 | 150 | | | + company1 | 07-04-2023 | 140 | | | + company1 | 07-05-2023 | 150 | | | + company1 | 07-06-2023 | 90 | 90 | 130 | 07-07-2023 + company1 | 07-07-2023 | 110 | | | + company1 | 07-08-2023 | 130 | | | + company1 | 07-09-2023 | 120 | | | + company1 | 07-10-2023 | 130 | | | + company2 | 07-01-2023 | 50 | 50 | 1500 | 07-02-2023 + company2 | 07-02-2023 | 2000 | | | + company2 | 07-03-2023 | 1500 | | | + company2 | 07-04-2023 | 1400 | | | + company2 | 07-05-2023 | 1500 | | | + company2 | 07-06-2023 | 60 | 60 | 1300 | 07-07-2023 + company2 | 07-07-2023 | 1100 | | | + company2 | 07-08-2023 | 1300 | | | + company2 | 07-09-2023 | 1200 | | | + company2 | 07-10-2023 | 1300 | | | +(20 rows) + +-- basic test using PREV. Use '*' +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w, + nth_value(tdate, 2) OVER w AS nth_second + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UP* DOWN+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + company | tdate | price | first_value | last_value | nth_second +----------+------------+-------+-------------+------------+------------ + company1 | 07-01-2023 | 100 | 100 | 140 | 07-02-2023 + company1 | 07-02-2023 | 200 | | | + company1 | 07-03-2023 | 150 | | | + company1 | 07-04-2023 | 140 | | | + company1 | 07-05-2023 | 150 | 150 | 90 | 07-06-2023 + company1 | 07-06-2023 | 90 | | | + company1 | 07-07-2023 | 110 | 110 | 120 | 07-08-2023 + company1 | 07-08-2023 | 130 | | | + company1 | 07-09-2023 | 120 | | | + company1 | 07-10-2023 | 130 | | | + company2 | 07-01-2023 | 50 | 50 | 1400 | 07-02-2023 + company2 | 07-02-2023 | 2000 | | | + company2 | 07-03-2023 | 1500 | | | + company2 | 07-04-2023 | 1400 | | | + company2 | 07-05-2023 | 1500 | 1500 | 60 | 07-06-2023 + company2 | 07-06-2023 | 60 | | | + company2 | 07-07-2023 | 1100 | 1100 | 1200 | 07-08-2023 + company2 | 07-08-2023 | 1300 | | | + company2 | 07-09-2023 | 1200 | | | + company2 | 07-10-2023 | 1300 | | | +(20 rows) + +-- basic test with none greedy pattern +SELECT company, tdate, price, count(*) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (A A A) + DEFINE + A AS price >= 140 AND price <= 150 +); + company | tdate | price | count +----------+------------+-------+------- + company1 | 07-01-2023 | 100 | 0 + company1 | 07-02-2023 | 200 | 0 + company1 | 07-03-2023 | 150 | 3 + company1 | 07-04-2023 | 140 | + company1 | 07-05-2023 | 150 | + company1 | 07-06-2023 | 90 | 0 + company1 | 07-07-2023 | 110 | 0 + company1 | 07-08-2023 | 130 | 0 + company1 | 07-09-2023 | 120 | 0 + company1 | 07-10-2023 | 130 | 0 + company2 | 07-01-2023 | 50 | 0 + company2 | 07-02-2023 | 2000 | 0 + company2 | 07-03-2023 | 1500 | 0 + company2 | 07-04-2023 | 1400 | 0 + company2 | 07-05-2023 | 1500 | 0 + company2 | 07-06-2023 | 60 | 0 + company2 | 07-07-2023 | 1100 | 0 + company2 | 07-08-2023 | 1300 | 0 + company2 | 07-09-2023 | 1200 | 0 + company2 | 07-10-2023 | 1300 | 0 +(20 rows) + +-- last_value() should remain consistent +SELECT company, tdate, price, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UP+ DOWN+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + company | tdate | price | last_value +----------+------------+-------+------------ + company1 | 07-01-2023 | 100 | 140 + company1 | 07-02-2023 | 200 | + company1 | 07-03-2023 | 150 | + company1 | 07-04-2023 | 140 | + company1 | 07-05-2023 | 150 | + company1 | 07-06-2023 | 90 | 120 + company1 | 07-07-2023 | 110 | + company1 | 07-08-2023 | 130 | + company1 | 07-09-2023 | 120 | + company1 | 07-10-2023 | 130 | + company2 | 07-01-2023 | 50 | 1400 + company2 | 07-02-2023 | 2000 | + company2 | 07-03-2023 | 1500 | + company2 | 07-04-2023 | 1400 | + company2 | 07-05-2023 | 1500 | + company2 | 07-06-2023 | 60 | 1200 + company2 | 07-07-2023 | 1100 | + company2 | 07-08-2023 | 1300 | + company2 | 07-09-2023 | 1200 | + company2 | 07-10-2023 | 1300 | +(20 rows) + +-- omit "START" in DEFINE but it is ok because "START AS TRUE" is +-- implicitly defined. per spec. +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w, + nth_value(tdate, 2) OVER w AS nth_second + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UP+ DOWN+) + DEFINE + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + company | tdate | price | first_value | last_value | nth_second +----------+------------+-------+-------------+------------+------------ + company1 | 07-01-2023 | 100 | 100 | 140 | 07-02-2023 + company1 | 07-02-2023 | 200 | | | + company1 | 07-03-2023 | 150 | | | + company1 | 07-04-2023 | 140 | | | + company1 | 07-05-2023 | 150 | | | + company1 | 07-06-2023 | 90 | 90 | 120 | 07-07-2023 + company1 | 07-07-2023 | 110 | | | + company1 | 07-08-2023 | 130 | | | + company1 | 07-09-2023 | 120 | | | + company1 | 07-10-2023 | 130 | | | + company2 | 07-01-2023 | 50 | 50 | 1400 | 07-02-2023 + company2 | 07-02-2023 | 2000 | | | + company2 | 07-03-2023 | 1500 | | | + company2 | 07-04-2023 | 1400 | | | + company2 | 07-05-2023 | 1500 | | | + company2 | 07-06-2023 | 60 | 60 | 1200 | 07-07-2023 + company2 | 07-07-2023 | 1100 | | | + company2 | 07-08-2023 | 1300 | | | + company2 | 07-09-2023 | 1200 | | | + company2 | 07-10-2023 | 1300 | | | +(20 rows) + +-- the first row start with less than or equal to 100 +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (LOWPRICE UP+ DOWN+) + DEFINE + LOWPRICE AS price <= 100, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + company | tdate | price | first_value | last_value +----------+------------+-------+-------------+------------ + company1 | 07-01-2023 | 100 | 100 | 140 + company1 | 07-02-2023 | 200 | | + company1 | 07-03-2023 | 150 | | + company1 | 07-04-2023 | 140 | | + company1 | 07-05-2023 | 150 | | + company1 | 07-06-2023 | 90 | 90 | 120 + company1 | 07-07-2023 | 110 | | + company1 | 07-08-2023 | 130 | | + company1 | 07-09-2023 | 120 | | + company1 | 07-10-2023 | 130 | | + company2 | 07-01-2023 | 50 | 50 | 1400 + company2 | 07-02-2023 | 2000 | | + company2 | 07-03-2023 | 1500 | | + company2 | 07-04-2023 | 1400 | | + company2 | 07-05-2023 | 1500 | | + company2 | 07-06-2023 | 60 | 60 | 1200 + company2 | 07-07-2023 | 1100 | | + company2 | 07-08-2023 | 1300 | | + company2 | 07-09-2023 | 1200 | | + company2 | 07-10-2023 | 1300 | | +(20 rows) + +-- second row raises 120% +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (LOWPRICE UP+ DOWN+) + DEFINE + LOWPRICE AS price <= 100, + UP AS price > PREV(price) * 1.2, + DOWN AS price < PREV(price) +); + company | tdate | price | first_value | last_value +----------+------------+-------+-------------+------------ + company1 | 07-01-2023 | 100 | 100 | 140 + company1 | 07-02-2023 | 200 | | + company1 | 07-03-2023 | 150 | | + company1 | 07-04-2023 | 140 | | + company1 | 07-05-2023 | 150 | | + company1 | 07-06-2023 | 90 | | + company1 | 07-07-2023 | 110 | | + company1 | 07-08-2023 | 130 | | + company1 | 07-09-2023 | 120 | | + company1 | 07-10-2023 | 130 | | + company2 | 07-01-2023 | 50 | 50 | 1400 + company2 | 07-02-2023 | 2000 | | + company2 | 07-03-2023 | 1500 | | + company2 | 07-04-2023 | 1400 | | + company2 | 07-05-2023 | 1500 | | + company2 | 07-06-2023 | 60 | | + company2 | 07-07-2023 | 1100 | | + company2 | 07-08-2023 | 1300 | | + company2 | 07-09-2023 | 1200 | | + company2 | 07-10-2023 | 1300 | | +(20 rows) + +-- using NEXT +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UPDOWN) + DEFINE + START AS TRUE, + UPDOWN AS price > PREV(price) AND price > NEXT(price) +); + company | tdate | price | first_value | last_value +----------+------------+-------+-------------+------------ + company1 | 07-01-2023 | 100 | 100 | 200 + company1 | 07-02-2023 | 200 | | + company1 | 07-03-2023 | 150 | | + company1 | 07-04-2023 | 140 | 140 | 150 + company1 | 07-05-2023 | 150 | | + company1 | 07-06-2023 | 90 | | + company1 | 07-07-2023 | 110 | 110 | 130 + company1 | 07-08-2023 | 130 | | + company1 | 07-09-2023 | 120 | | + company1 | 07-10-2023 | 130 | | + company2 | 07-01-2023 | 50 | 50 | 2000 + company2 | 07-02-2023 | 2000 | | + company2 | 07-03-2023 | 1500 | | + company2 | 07-04-2023 | 1400 | 1400 | 1500 + company2 | 07-05-2023 | 1500 | | + company2 | 07-06-2023 | 60 | | + company2 | 07-07-2023 | 1100 | 1100 | 1300 + company2 | 07-08-2023 | 1300 | | + company2 | 07-09-2023 | 1200 | | + company2 | 07-10-2023 | 1300 | | +(20 rows) + +-- using AFTER MATCH SKIP TO NEXT ROW +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + AFTER MATCH SKIP TO NEXT ROW + INITIAL + PATTERN (START UPDOWN) + DEFINE + START AS TRUE, + UPDOWN AS price > PREV(price) AND price > NEXT(price) +); + company | tdate | price | first_value | last_value +----------+------------+-------+-------------+------------ + company1 | 07-01-2023 | 100 | 100 | 200 + company1 | 07-02-2023 | 200 | | + company1 | 07-03-2023 | 150 | | + company1 | 07-04-2023 | 140 | 140 | 150 + company1 | 07-05-2023 | 150 | | + company1 | 07-06-2023 | 90 | | + company1 | 07-07-2023 | 110 | 110 | 130 + company1 | 07-08-2023 | 130 | | + company1 | 07-09-2023 | 120 | | + company1 | 07-10-2023 | 130 | | + company2 | 07-01-2023 | 50 | 50 | 2000 + company2 | 07-02-2023 | 2000 | | + company2 | 07-03-2023 | 1500 | | + company2 | 07-04-2023 | 1400 | 1400 | 1500 + company2 | 07-05-2023 | 1500 | | + company2 | 07-06-2023 | 60 | | + company2 | 07-07-2023 | 1100 | 1100 | 1300 + company2 | 07-08-2023 | 1300 | | + company2 | 07-09-2023 | 1200 | | + company2 | 07-10-2023 | 1300 | | +(20 rows) + +-- match everything +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + AFTER MATCH SKIP PAST LAST ROW + INITIAL + PATTERN (A+) + DEFINE + A AS TRUE +); + company | tdate | price | first_value | last_value +----------+------------+-------+-------------+------------ + company1 | 07-01-2023 | 100 | 100 | 130 + company1 | 07-02-2023 | 200 | | + company1 | 07-03-2023 | 150 | | + company1 | 07-04-2023 | 140 | | + company1 | 07-05-2023 | 150 | | + company1 | 07-06-2023 | 90 | | + company1 | 07-07-2023 | 110 | | + company1 | 07-08-2023 | 130 | | + company1 | 07-09-2023 | 120 | | + company1 | 07-10-2023 | 130 | | + company2 | 07-01-2023 | 50 | 50 | 1300 + company2 | 07-02-2023 | 2000 | | + company2 | 07-03-2023 | 1500 | | + company2 | 07-04-2023 | 1400 | | + company2 | 07-05-2023 | 1500 | | + company2 | 07-06-2023 | 60 | | + company2 | 07-07-2023 | 1100 | | + company2 | 07-08-2023 | 1300 | | + company2 | 07-09-2023 | 1200 | | + company2 | 07-10-2023 | 1300 | | +(20 rows) + +-- backtracking with reclassification of rows +-- using AFTER MATCH SKIP PAST LAST ROW +SELECT company, tdate, price, first_value(tdate) OVER w, last_value(tdate) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + AFTER MATCH SKIP PAST LAST ROW + INITIAL + PATTERN (A+ B+) + DEFINE + A AS price > 100, + B AS price > 100 +); + company | tdate | price | first_value | last_value +----------+------------+-------+-------------+------------ + company1 | 07-01-2023 | 100 | | + company1 | 07-02-2023 | 200 | 07-02-2023 | 07-05-2023 + company1 | 07-03-2023 | 150 | | + company1 | 07-04-2023 | 140 | | + company1 | 07-05-2023 | 150 | | + company1 | 07-06-2023 | 90 | | + company1 | 07-07-2023 | 110 | 07-07-2023 | 07-10-2023 + company1 | 07-08-2023 | 130 | | + company1 | 07-09-2023 | 120 | | + company1 | 07-10-2023 | 130 | | + company2 | 07-01-2023 | 50 | | + company2 | 07-02-2023 | 2000 | 07-02-2023 | 07-05-2023 + company2 | 07-03-2023 | 1500 | | + company2 | 07-04-2023 | 1400 | | + company2 | 07-05-2023 | 1500 | | + company2 | 07-06-2023 | 60 | | + company2 | 07-07-2023 | 1100 | 07-07-2023 | 07-10-2023 + company2 | 07-08-2023 | 1300 | | + company2 | 07-09-2023 | 1200 | | + company2 | 07-10-2023 | 1300 | | +(20 rows) + +-- backtracking with reclassification of rows +-- using AFTER MATCH SKIP TO NEXT ROW +SELECT company, tdate, price, first_value(tdate) OVER w, last_value(tdate) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + AFTER MATCH SKIP TO NEXT ROW + INITIAL + PATTERN (A+ B+) + DEFINE + A AS price > 100, + B AS price > 100 +); + company | tdate | price | first_value | last_value +----------+------------+-------+-------------+------------ + company1 | 07-01-2023 | 100 | | + company1 | 07-02-2023 | 200 | 07-02-2023 | 07-05-2023 + company1 | 07-03-2023 | 150 | 07-03-2023 | 07-05-2023 + company1 | 07-04-2023 | 140 | 07-04-2023 | 07-05-2023 + company1 | 07-05-2023 | 150 | | + company1 | 07-06-2023 | 90 | | + company1 | 07-07-2023 | 110 | 07-07-2023 | 07-10-2023 + company1 | 07-08-2023 | 130 | 07-08-2023 | 07-10-2023 + company1 | 07-09-2023 | 120 | 07-09-2023 | 07-10-2023 + company1 | 07-10-2023 | 130 | | + company2 | 07-01-2023 | 50 | | + company2 | 07-02-2023 | 2000 | 07-02-2023 | 07-05-2023 + company2 | 07-03-2023 | 1500 | 07-03-2023 | 07-05-2023 + company2 | 07-04-2023 | 1400 | 07-04-2023 | 07-05-2023 + company2 | 07-05-2023 | 1500 | | + company2 | 07-06-2023 | 60 | | + company2 | 07-07-2023 | 1100 | 07-07-2023 | 07-10-2023 + company2 | 07-08-2023 | 1300 | 07-08-2023 | 07-10-2023 + company2 | 07-09-2023 | 1200 | 07-09-2023 | 07-10-2023 + company2 | 07-10-2023 | 1300 | | +(20 rows) + +-- ROWS BETWEEN CURRENT ROW AND offset FOLLOWING +SELECT company, tdate, price, first_value(tdate) OVER w, last_value(tdate) OVER w, + count(*) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING + AFTER MATCH SKIP PAST LAST ROW + PATTERN (START UP+ DOWN+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + company | tdate | price | first_value | last_value | count +----------+------------+-------+-------------+------------+------- + company1 | 07-01-2023 | 100 | 07-01-2023 | 07-03-2023 | 3 + company1 | 07-02-2023 | 200 | | | + company1 | 07-03-2023 | 150 | | | + company1 | 07-04-2023 | 140 | 07-04-2023 | 07-06-2023 | 3 + company1 | 07-05-2023 | 150 | | | + company1 | 07-06-2023 | 90 | | | + company1 | 07-07-2023 | 110 | 07-07-2023 | 07-09-2023 | 3 + company1 | 07-08-2023 | 130 | | | + company1 | 07-09-2023 | 120 | | | + company1 | 07-10-2023 | 130 | | | 0 + company2 | 07-01-2023 | 50 | 07-01-2023 | 07-03-2023 | 3 + company2 | 07-02-2023 | 2000 | | | + company2 | 07-03-2023 | 1500 | | | + company2 | 07-04-2023 | 1400 | 07-04-2023 | 07-06-2023 | 3 + company2 | 07-05-2023 | 1500 | | | + company2 | 07-06-2023 | 60 | | | + company2 | 07-07-2023 | 1100 | 07-07-2023 | 07-09-2023 | 3 + company2 | 07-08-2023 | 1300 | | | + company2 | 07-09-2023 | 1200 | | | + company2 | 07-10-2023 | 1300 | | | 0 +(20 rows) + +-- +-- Aggregates +-- +-- using AFTER MATCH SKIP PAST LAST ROW +SELECT company, tdate, price, + first_value(price) OVER w, + last_value(price) OVER w, + max(price) OVER w, + min(price) OVER w, + sum(price) OVER w, + avg(price) OVER w, + count(price) OVER w +FROM stock +WINDOW w AS ( +PARTITION BY company +ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +AFTER MATCH SKIP PAST LAST ROW +INITIAL +PATTERN (START UP+ DOWN+) +DEFINE +START AS TRUE, +UP AS price > PREV(price), +DOWN AS price < PREV(price) +); + company | tdate | price | first_value | last_value | max | min | sum | avg | count +----------+------------+-------+-------------+------------+------+-----+------+-----------------------+------- + company1 | 07-01-2023 | 100 | 100 | 140 | 200 | 100 | 590 | 147.5000000000000000 | 4 + company1 | 07-02-2023 | 200 | | | | | | | + company1 | 07-03-2023 | 150 | | | | | | | + company1 | 07-04-2023 | 140 | | | | | | | + company1 | 07-05-2023 | 150 | | | | | | | 0 + company1 | 07-06-2023 | 90 | 90 | 120 | 130 | 90 | 450 | 112.5000000000000000 | 4 + company1 | 07-07-2023 | 110 | | | | | | | + company1 | 07-08-2023 | 130 | | | | | | | + company1 | 07-09-2023 | 120 | | | | | | | + company1 | 07-10-2023 | 130 | | | | | | | 0 + company2 | 07-01-2023 | 50 | 50 | 1400 | 2000 | 50 | 4950 | 1237.5000000000000000 | 4 + company2 | 07-02-2023 | 2000 | | | | | | | + company2 | 07-03-2023 | 1500 | | | | | | | + company2 | 07-04-2023 | 1400 | | | | | | | + company2 | 07-05-2023 | 1500 | | | | | | | 0 + company2 | 07-06-2023 | 60 | 60 | 1200 | 1300 | 60 | 3660 | 915.0000000000000000 | 4 + company2 | 07-07-2023 | 1100 | | | | | | | + company2 | 07-08-2023 | 1300 | | | | | | | + company2 | 07-09-2023 | 1200 | | | | | | | + company2 | 07-10-2023 | 1300 | | | | | | | 0 +(20 rows) + +-- using AFTER MATCH SKIP TO NEXT ROW +SELECT company, tdate, price, + first_value(price) OVER w, + last_value(price) OVER w, + max(price) OVER w, + min(price) OVER w, + sum(price) OVER w, + avg(price) OVER w, + count(price) OVER w +FROM stock +WINDOW w AS ( +PARTITION BY company +ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +AFTER MATCH SKIP TO NEXT ROW +INITIAL +PATTERN (START UP+ DOWN+) +DEFINE +START AS TRUE, +UP AS price > PREV(price), +DOWN AS price < PREV(price) +); + company | tdate | price | first_value | last_value | max | min | sum | avg | count +----------+------------+-------+-------------+------------+------+------+------+-----------------------+------- + company1 | 07-01-2023 | 100 | 100 | 140 | 200 | 100 | 590 | 147.5000000000000000 | 4 + company1 | 07-02-2023 | 200 | | | | | | | 0 + company1 | 07-03-2023 | 150 | | | | | | | 0 + company1 | 07-04-2023 | 140 | 140 | 90 | 150 | 90 | 380 | 126.6666666666666667 | 3 + company1 | 07-05-2023 | 150 | | | | | | | 0 + company1 | 07-06-2023 | 90 | 90 | 120 | 130 | 90 | 450 | 112.5000000000000000 | 4 + company1 | 07-07-2023 | 110 | 110 | 120 | 130 | 110 | 360 | 120.0000000000000000 | 3 + company1 | 07-08-2023 | 130 | | | | | | | 0 + company1 | 07-09-2023 | 120 | | | | | | | 0 + company1 | 07-10-2023 | 130 | | | | | | | 0 + company2 | 07-01-2023 | 50 | 50 | 1400 | 2000 | 50 | 4950 | 1237.5000000000000000 | 4 + company2 | 07-02-2023 | 2000 | | | | | | | 0 + company2 | 07-03-2023 | 1500 | | | | | | | 0 + company2 | 07-04-2023 | 1400 | 1400 | 60 | 1500 | 60 | 2960 | 986.6666666666666667 | 3 + company2 | 07-05-2023 | 1500 | | | | | | | 0 + company2 | 07-06-2023 | 60 | 60 | 1200 | 1300 | 60 | 3660 | 915.0000000000000000 | 4 + company2 | 07-07-2023 | 1100 | 1100 | 1200 | 1300 | 1100 | 3600 | 1200.0000000000000000 | 3 + company2 | 07-08-2023 | 1300 | | | | | | | 0 + company2 | 07-09-2023 | 1200 | | | | | | | 0 + company2 | 07-10-2023 | 1300 | | | | | | | 0 +(20 rows) + +-- JOIN case +CREATE TEMP TABLE t1 (i int, v1 int); +CREATE TEMP TABLE t2 (j int, v2 int); +INSERT INTO t1 VALUES(1,10); +INSERT INTO t1 VALUES(1,11); +INSERT INTO t1 VALUES(1,12); +INSERT INTO t2 VALUES(2,10); +INSERT INTO t2 VALUES(2,11); +INSERT INTO t2 VALUES(2,12); +SELECT * FROM t1, t2 WHERE t1.v1 <= 11 AND t2.v2 <= 11; + i | v1 | j | v2 +---+----+---+---- + 1 | 10 | 2 | 10 + 1 | 10 | 2 | 11 + 1 | 11 | 2 | 10 + 1 | 11 | 2 | 11 +(4 rows) + +SELECT *, count(*) OVER w FROM t1, t2 +WINDOW w AS ( + PARTITION BY t1.i + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (A) + DEFINE + A AS v1 <= 11 AND v2 <= 11 +); + i | v1 | j | v2 | count +---+----+---+----+------- + 1 | 10 | 2 | 10 | 1 + 1 | 10 | 2 | 11 | 1 + 1 | 10 | 2 | 12 | 0 + 1 | 11 | 2 | 10 | 1 + 1 | 11 | 2 | 11 | 1 + 1 | 11 | 2 | 12 | 0 + 1 | 12 | 2 | 10 | 0 + 1 | 12 | 2 | 11 | 0 + 1 | 12 | 2 | 12 | 0 +(9 rows) + +-- WITH case +WITH wstock AS ( + SELECT * FROM stock WHERE tdate < '2023-07-08' +) +SELECT tdate, price, +first_value(tdate) OVER w, +count(*) OVER w + FROM wstock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UP+ DOWN+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + tdate | price | first_value | count +------------+-------+-------------+------- + 07-01-2023 | 100 | 07-01-2023 | 4 + 07-02-2023 | 200 | | + 07-03-2023 | 150 | | + 07-04-2023 | 140 | | + 07-05-2023 | 150 | | 0 + 07-06-2023 | 90 | | 0 + 07-07-2023 | 110 | | 0 + 07-01-2023 | 50 | 07-01-2023 | 4 + 07-02-2023 | 2000 | | + 07-03-2023 | 1500 | | + 07-04-2023 | 1400 | | + 07-05-2023 | 1500 | | 0 + 07-06-2023 | 60 | | 0 + 07-07-2023 | 1100 | | 0 +(14 rows) + +-- +-- Error cases +-- +-- row pattern definition variable name must not appear more than once +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + ORDER BY tdate + INITIAL + PATTERN (START UP+ DOWN+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price), + UP AS price > PREV(price) +); +ERROR: syntax error at or near "ORDER" +LINE 6: ORDER BY tdate + ^ +-- pattern variable name must appear in DEFINE +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UP+ DOWN+ END) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); +ERROR: syntax error at or near "END" +LINE 8: PATTERN (START UP+ DOWN+ END) + ^ +-- FRAME must start at current row when row patttern recognition is used +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UP+ DOWN+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); +ERROR: FRAME must start at current row when row patttern recognition is used +-- SEEK is not supported +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + AFTER MATCH SKIP TO NEXT ROW + SEEK + PATTERN (START UP+ DOWN+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); +ERROR: SEEK is not supported +LINE 8: SEEK + ^ +HINT: Use INITIAL. diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index f0987ff537..a6542f3dea 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -98,7 +98,7 @@ test: publication subscription # Another group of parallel tests # select_views depends on create_view # ---------- -test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock indirect_toast equivclass +test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock indirect_toast equivclass rpr # ---------- # Another group of parallel tests (JSON related) diff --git a/src/test/regress/sql/rpr.sql b/src/test/regress/sql/rpr.sql new file mode 100644 index 0000000000..0a69cc0e11 --- /dev/null +++ b/src/test/regress/sql/rpr.sql @@ -0,0 +1,392 @@ +-- +-- Test for row pattern definition clause +-- + +CREATE TEMP TABLE stock ( + company TEXT, + tdate DATE, + price INTEGER +); +INSERT INTO stock VALUES ('company1', '2023-07-01', 100); +INSERT INTO stock VALUES ('company1', '2023-07-02', 200); +INSERT INTO stock VALUES ('company1', '2023-07-03', 150); +INSERT INTO stock VALUES ('company1', '2023-07-04', 140); +INSERT INTO stock VALUES ('company1', '2023-07-05', 150); +INSERT INTO stock VALUES ('company1', '2023-07-06', 90); +INSERT INTO stock VALUES ('company1', '2023-07-07', 110); +INSERT INTO stock VALUES ('company1', '2023-07-08', 130); +INSERT INTO stock VALUES ('company1', '2023-07-09', 120); +INSERT INTO stock VALUES ('company1', '2023-07-10', 130); +INSERT INTO stock VALUES ('company2', '2023-07-01', 50); +INSERT INTO stock VALUES ('company2', '2023-07-02', 2000); +INSERT INTO stock VALUES ('company2', '2023-07-03', 1500); +INSERT INTO stock VALUES ('company2', '2023-07-04', 1400); +INSERT INTO stock VALUES ('company2', '2023-07-05', 1500); +INSERT INTO stock VALUES ('company2', '2023-07-06', 60); +INSERT INTO stock VALUES ('company2', '2023-07-07', 1100); +INSERT INTO stock VALUES ('company2', '2023-07-08', 1300); +INSERT INTO stock VALUES ('company2', '2023-07-09', 1200); +INSERT INTO stock VALUES ('company2', '2023-07-10', 1300); + +SELECT * FROM stock; + +-- basic test using PREV +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w, + nth_value(tdate, 2) OVER w AS nth_second + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UP+ DOWN+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + +-- basic test using PREV. UP appears twice +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w, + nth_value(tdate, 2) OVER w AS nth_second + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UP+ DOWN+ UP+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + +-- basic test using PREV. Use '*' +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w, + nth_value(tdate, 2) OVER w AS nth_second + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UP* DOWN+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + +-- basic test with none greedy pattern +SELECT company, tdate, price, count(*) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (A A A) + DEFINE + A AS price >= 140 AND price <= 150 +); + +-- last_value() should remain consistent +SELECT company, tdate, price, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UP+ DOWN+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + +-- omit "START" in DEFINE but it is ok because "START AS TRUE" is +-- implicitly defined. per spec. +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w, + nth_value(tdate, 2) OVER w AS nth_second + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UP+ DOWN+) + DEFINE + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + +-- the first row start with less than or equal to 100 +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (LOWPRICE UP+ DOWN+) + DEFINE + LOWPRICE AS price <= 100, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + +-- second row raises 120% +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (LOWPRICE UP+ DOWN+) + DEFINE + LOWPRICE AS price <= 100, + UP AS price > PREV(price) * 1.2, + DOWN AS price < PREV(price) +); + +-- using NEXT +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UPDOWN) + DEFINE + START AS TRUE, + UPDOWN AS price > PREV(price) AND price > NEXT(price) +); + +-- using AFTER MATCH SKIP TO NEXT ROW +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + AFTER MATCH SKIP TO NEXT ROW + INITIAL + PATTERN (START UPDOWN) + DEFINE + START AS TRUE, + UPDOWN AS price > PREV(price) AND price > NEXT(price) +); + +-- match everything + +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + AFTER MATCH SKIP PAST LAST ROW + INITIAL + PATTERN (A+) + DEFINE + A AS TRUE +); + +-- backtracking with reclassification of rows +-- using AFTER MATCH SKIP PAST LAST ROW +SELECT company, tdate, price, first_value(tdate) OVER w, last_value(tdate) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + AFTER MATCH SKIP PAST LAST ROW + INITIAL + PATTERN (A+ B+) + DEFINE + A AS price > 100, + B AS price > 100 +); + +-- backtracking with reclassification of rows +-- using AFTER MATCH SKIP TO NEXT ROW +SELECT company, tdate, price, first_value(tdate) OVER w, last_value(tdate) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + AFTER MATCH SKIP TO NEXT ROW + INITIAL + PATTERN (A+ B+) + DEFINE + A AS price > 100, + B AS price > 100 +); + +-- ROWS BETWEEN CURRENT ROW AND offset FOLLOWING +SELECT company, tdate, price, first_value(tdate) OVER w, last_value(tdate) OVER w, + count(*) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING + AFTER MATCH SKIP PAST LAST ROW + PATTERN (START UP+ DOWN+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + +-- +-- Aggregates +-- + +-- using AFTER MATCH SKIP PAST LAST ROW +SELECT company, tdate, price, + first_value(price) OVER w, + last_value(price) OVER w, + max(price) OVER w, + min(price) OVER w, + sum(price) OVER w, + avg(price) OVER w, + count(price) OVER w +FROM stock +WINDOW w AS ( +PARTITION BY company +ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +AFTER MATCH SKIP PAST LAST ROW +INITIAL +PATTERN (START UP+ DOWN+) +DEFINE +START AS TRUE, +UP AS price > PREV(price), +DOWN AS price < PREV(price) +); + +-- using AFTER MATCH SKIP TO NEXT ROW +SELECT company, tdate, price, + first_value(price) OVER w, + last_value(price) OVER w, + max(price) OVER w, + min(price) OVER w, + sum(price) OVER w, + avg(price) OVER w, + count(price) OVER w +FROM stock +WINDOW w AS ( +PARTITION BY company +ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +AFTER MATCH SKIP TO NEXT ROW +INITIAL +PATTERN (START UP+ DOWN+) +DEFINE +START AS TRUE, +UP AS price > PREV(price), +DOWN AS price < PREV(price) +); + +-- JOIN case +CREATE TEMP TABLE t1 (i int, v1 int); +CREATE TEMP TABLE t2 (j int, v2 int); +INSERT INTO t1 VALUES(1,10); +INSERT INTO t1 VALUES(1,11); +INSERT INTO t1 VALUES(1,12); +INSERT INTO t2 VALUES(2,10); +INSERT INTO t2 VALUES(2,11); +INSERT INTO t2 VALUES(2,12); + +SELECT * FROM t1, t2 WHERE t1.v1 <= 11 AND t2.v2 <= 11; + +SELECT *, count(*) OVER w FROM t1, t2 +WINDOW w AS ( + PARTITION BY t1.i + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (A) + DEFINE + A AS v1 <= 11 AND v2 <= 11 +); + +-- WITH case +WITH wstock AS ( + SELECT * FROM stock WHERE tdate < '2023-07-08' +) +SELECT tdate, price, +first_value(tdate) OVER w, +count(*) OVER w + FROM wstock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UP+ DOWN+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + +-- +-- Error cases +-- + +-- row pattern definition variable name must not appear more than once +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + ORDER BY tdate + INITIAL + PATTERN (START UP+ DOWN+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price), + UP AS price > PREV(price) +); + +-- pattern variable name must appear in DEFINE +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UP+ DOWN+ END) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + +-- FRAME must start at current row when row patttern recognition is used +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING + INITIAL + PATTERN (START UP+ DOWN+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); + +-- SEEK is not supported +SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w + FROM stock + WINDOW w AS ( + PARTITION BY company + ORDER BY tdate + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + AFTER MATCH SKIP TO NEXT ROW + SEEK + PATTERN (START UP+ DOWN+) + DEFINE + START AS TRUE, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); -- 2.25.1 ----Next_Part(Mon_Jan_22_19_26_18_2024_011)-- Content-Type: Text/X-Patch; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="v13-0008-Allow-to-print-raw-parse-tree.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
* [PATCH 1/3] stress test for repack concurrently @ 2025-12-13 17:13 Mikhail Nikalayeu <[email protected]> 0 siblings, 0 replies; 94+ messages in thread From: Mikhail Nikalayeu @ 2025-12-13 17:13 UTC (permalink / raw) --- contrib/amcheck/meson.build | 1 + contrib/amcheck/t/007_repack_1.pl | 121 ++++++++++++++++++++++++++++++ doc/src/sgml/regress.sgml | 16 ++++ 3 files changed, 138 insertions(+) create mode 100644 contrib/amcheck/t/007_repack_1.pl diff --git a/contrib/amcheck/meson.build b/contrib/amcheck/meson.build index d5137ef691d..cb4bc32e98a 100644 --- a/contrib/amcheck/meson.build +++ b/contrib/amcheck/meson.build @@ -50,6 +50,7 @@ tests += { 't/004_verify_nbtree_unique.pl', 't/005_pitr.pl', 't/006_verify_gin.pl', + 't/007_repack_1.pl', ], }, } diff --git a/contrib/amcheck/t/007_repack_1.pl b/contrib/amcheck/t/007_repack_1.pl new file mode 100644 index 00000000000..17f3caa3a38 --- /dev/null +++ b/contrib/amcheck/t/007_repack_1.pl @@ -0,0 +1,121 @@ +# Copyright (c) 2021-2026, PostgreSQL Global Development Group + +# Test REPACK CONCURRENTLY with concurrent modifications +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; + +use Test::More; + +# PG_TEST_EXTRA carries a numerical stress value; 0 disables the test, +# 1 means the test takes about 6 seconds, and the increase should be +# roughly linear. +my $matched = ($ENV{PG_TEST_EXTRA} // '') =~ /\bstress_concurrently(?:=(?<stressval>[0-9]*))?\b/; +my $stressval = $+{stressval} // 1; +if (!$matched or $stressval == 0) +{ + plan skip_all => 'skipping disabled REPACK CONCURRENTLY stress test'; +} + +note "stressval is $stressval"; + +my $node; + +# Test set-up +$node = PostgreSQL::Test::Cluster->new('CIC_test'); +$node->init; +$node->append_conf('postgresql.conf', + 'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default)); +$node->append_conf( + 'postgresql.conf', qq( +wal_level = logical +)); + +my $nrows = 10_000 * $stressval; +my $duration = 6 * $stressval; +my $no_hot = int(rand(2)); + +$node->start; +$node->safe_psql('postgres', q(CREATE TABLE tbl(id int PRIMARY KEY, val int))); + +if ($no_hot) +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(val);)); +} +else +{ + $node->safe_psql('postgres', q(CREATE INDEX test_idx ON tbl(id);)); +} + + +# Load amcheck +$node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); + +# Insert $nrows rows into tbl +$node->safe_psql('postgres', qq( + INSERT INTO tbl SELECT g, g FROM generate_series(1, $nrows) g +)); + +my $sum = $node->safe_psql('postgres', q( + SELECT SUM(val) AS sum FROM tbl +)); + + +$node->pgbench( +"--no-vacuum --client=30 --jobs=4 --exit-on-abort -T $duration", +0, +[qr{actually processed}], +[qr{^$}], +'concurrent operations with REINDEX/CREATE INDEX CONCURRENTLY', +{ + 'concurrent_ops' => qq( + SELECT pg_try_advisory_lock(42)::integer AS gotlock \\gset + \\if :gotlock + REPACK (CONCURRENTLY) tbl USING INDEX tbl_pkey; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl USING INDEX test_idx; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + REPACK (CONCURRENTLY) tbl; + SELECT bt_index_parent_check('tbl_pkey', heapallindexed => true); + SELECT bt_index_parent_check('test_idx', heapallindexed => true); + \\sleep 10 ms + + SELECT pg_advisory_unlock(42); + \\else + \\set num_a random(1, $nrows) + \\set num_b random(1, $nrows) + \\set diff random(1, 10000) + BEGIN; + UPDATE tbl SET val = val + :diff WHERE id = :num_a; + \\sleep 1 ms + UPDATE tbl SET val = val - :diff WHERE id = :num_b; + \\sleep 1 ms + COMMIT; + + BEGIN + --TRANSACTION ISOLATION LEVEL REPEATABLE READ + ; + SELECT 1; + \\sleep 1 ms + SELECT COALESCE(SUM(val), 0) AS sum FROM tbl \\gset p_ + \\if :p_sum != $sum + COMMIT; + SELECT (:p_sum) / 0; + \\endif + + COMMIT; + \\endif + ) +}); + +$node->stop; + +done_testing(); diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index c74941bfbf2..c5b1b79a60c 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -386,6 +386,22 @@ make check-world PG_TEST_EXTRA='kerberos ldap ssl load_balance libpq_encryption' </listitem> </varlistentry> + <varlistentry> + <term><literal>stress_concurrently</literal><optional>=VALUE</optional></term> + <listitem> + <para> + Run some additional, moderately expensive tests for commands with a + <literal>CONCURRENTLY</literal> option. Optionally, an integer can be + given after an equal sign, which affects how long each such test + runs for. + Each test is calibrated so that, when given a value of 10, + it runs for approximately 60 seconds. + If no value is given, 1 is assumed. + Explicitly giving a value of 0 causes the test to be skipped. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>wal_consistency_checking</literal></term> <listitem> -- 2.47.3 --tbdehtstrqwksfdh Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="0002-one-more-stress-test-for-repack-concurrently.patch" ^ permalink raw reply [nested|flat] 94+ messages in thread
end of thread, other threads:[~2025-12-13 17:13 UTC | newest] Thread overview: 94+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2020-03-12 21:33 [PATCH 8/8] reverse arguments .. isn't the other order a bug? Alvaro Herrera <[email protected]> 2024-01-22 09:45 [PATCH v13 7/8] Row pattern recognition patch (tests). Tatsuo Ishii <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]> 2025-12-13 17:13 [PATCH 1/3] stress test for repack concurrently Mikhail Nikalayeu <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox