Hello! On Tue, Dec 16, 2025 at 3:48 AM Mihail Nikalayeu <[email protected]> wrote: > First version of TAP-based test in attachment. I think we should not > hurry with push - let's make sure it really stable now.
Second version - some issues fixed, added some log and diagnostic for the next fail. Mikhail.
From f532028260b8da0697772d0f5c0c16c58fc15f02 Mon Sep 17 00:00:00 2001 From: Mikhail Nikalayeu <[email protected]> Date: Mon, 15 Dec 2025 12:03:04 +0100 Subject: [PATCH vnocfbo2] Replace flaky isolation tests with a TAP test for concurrent upserts The isolation tests verifying INSERT ON CONFLICT behavior during concurrent index creation/reindexing were prone to flakiness. The timing dependencies on injection points were difficult to guarantee in the isolation tester. Remove the disabled isolation tests and their specs/expected output. Add TAP test '010_index_concurrently_upsert.pl' to cover these scenarios more reliably. The TAP test now includes better session management and more robust injection point waiting logic. --- src/test/modules/injection_points/Makefile | 8 - .../index-concurrently-upsert-predicate.out | 123 --- .../index-concurrently-upsert-predicate_1.out | 124 --- .../expected/index-concurrently-upsert.out | 123 --- .../expected/index-concurrently-upsert_1.out | 124 --- ...ndex-concurrently-upsert-on-constraint.out | 238 ----- ...eindex-concurrently-upsert-partitioned.out | 238 ----- .../expected/reindex-concurrently-upsert.out | 238 ----- src/test/modules/injection_points/meson.build | 6 - .../index-concurrently-upsert-predicate.spec | 124 --- .../specs/index-concurrently-upsert.spec | 123 --- ...dex-concurrently-upsert-on-constraint.spec | 110 --- ...index-concurrently-upsert-partitioned.spec | 113 --- .../specs/reindex-concurrently-upsert.spec | 111 --- src/test/modules/test_misc/Makefile | 3 + src/test/modules/test_misc/meson.build | 3 + .../t/010_index_concurrently_upsert.pl | 894 ++++++++++++++++++ 17 files changed, 900 insertions(+), 1803 deletions(-) delete mode 100644 src/test/modules/injection_points/expected/index-concurrently-upsert-predicate.out delete mode 100644 src/test/modules/injection_points/expected/index-concurrently-upsert-predicate_1.out delete mode 100644 src/test/modules/injection_points/expected/index-concurrently-upsert.out delete mode 100644 src/test/modules/injection_points/expected/index-concurrently-upsert_1.out delete mode 100644 src/test/modules/injection_points/expected/reindex-concurrently-upsert-on-constraint.out delete mode 100644 src/test/modules/injection_points/expected/reindex-concurrently-upsert-partitioned.out delete mode 100644 src/test/modules/injection_points/expected/reindex-concurrently-upsert.out delete mode 100644 src/test/modules/injection_points/specs/index-concurrently-upsert-predicate.spec delete mode 100644 src/test/modules/injection_points/specs/index-concurrently-upsert.spec delete mode 100644 src/test/modules/injection_points/specs/reindex-concurrently-upsert-on-constraint.spec delete mode 100644 src/test/modules/injection_points/specs/reindex-concurrently-upsert-partitioned.spec delete mode 100644 src/test/modules/injection_points/specs/reindex-concurrently-upsert.spec create mode 100644 src/test/modules/test_misc/t/010_index_concurrently_upsert.pl diff --git a/src/test/modules/injection_points/Makefile b/src/test/modules/injection_points/Makefile index bfdb3f53377..3cb50d13e52 100644 --- a/src/test/modules/injection_points/Makefile +++ b/src/test/modules/injection_points/Makefile @@ -16,14 +16,6 @@ ISOLATION = basic \ inplace \ syscache-update-pruned -# Temporarily disabled because of flakiness -#ISOLATION =+ -# index-concurrently-upsert \ -# index-concurrently-upsert-predicate \ -# reindex-concurrently-upsert \ -# reindex-concurrently-upsert-on-constraint \ -# reindex-concurrently-upsert-partitioned - # The injection points are cluster-wide, so disable installcheck NO_INSTALLCHECK = 1 diff --git a/src/test/modules/injection_points/expected/index-concurrently-upsert-predicate.out b/src/test/modules/injection_points/expected/index-concurrently-upsert-predicate.out deleted file mode 100644 index 77e7d1a7815..00000000000 --- a/src/test/modules/injection_points/expected/index-concurrently-upsert-predicate.out +++ /dev/null @@ -1,123 +0,0 @@ -Parsed test spec with 5 sessions - -starting permutation: s1_attach_invalidate_catalog_snapshot s4_wakeup_s1_setup s3_start_create_index s1_start_upsert s4_wakeup_define_index_before_set_valid s2_start_upsert s5_wakeup_s1_from_invalidate_catalog_snapshot s4_wakeup_s2 s4_wakeup_s1 -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -step s1_attach_invalidate_catalog_snapshot: - SELECT injection_points_attach('invalidate-catalog-snapshot-end', 'wait'); - -injection_points_attach ------------------------ - -(1 row) - -step s4_wakeup_s1_setup: - SELECT CASE WHEN - (SELECT pid FROM pg_stat_activity - WHERE wait_event_type = 'InjectionPoint' AND - wait_event = 'invalidate-catalog-snapshot-end') IS NOT NULL - THEN injection_points_wakeup('invalidate-catalog-snapshot-end') - END; - -case ----- - -(1 row) - -step s3_start_create_index: - CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_special_duplicate ON test.tbl(abs(i)) WHERE i < 10000; - <waiting ...> -step s1_start_upsert: - INSERT INTO test.tbl VALUES(13,now()) ON CONFLICT (abs(i)) WHERE i < 100 DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_define_index_before_set_valid: - SELECT injection_points_detach('define-index-before-set-valid'); - SELECT injection_points_wakeup('define-index-before-set-valid'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s2_start_upsert: - INSERT INTO test.tbl VALUES(13,now()) ON CONFLICT (abs(i)) WHERE i < 100 DO UPDATE SET updated_at = now(); - <waiting ...> -step s5_wakeup_s1_from_invalidate_catalog_snapshot: - DO $$ - DECLARE - v_waiting_pid INTEGER; - BEGIN - LOOP - SELECT pid INTO v_waiting_pid - FROM pg_stat_activity - WHERE wait_event_type = 'InjectionPoint' - AND wait_event = 'invalidate-catalog-snapshot-end' - LIMIT 1; - EXIT WHEN v_waiting_pid IS NOT NULL; - PERFORM pg_sleep(100); - END LOOP; - END - $$; - - SELECT injection_points_detach('invalidate-catalog-snapshot-end'); - SELECT injection_points_wakeup('invalidate-catalog-snapshot-end'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s4_wakeup_s2: - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s4_wakeup_s1: - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s1_start_upsert: <... completed> -step s2_start_upsert: <... completed> -step s3_start_create_index: <... completed> diff --git a/src/test/modules/injection_points/expected/index-concurrently-upsert-predicate_1.out b/src/test/modules/injection_points/expected/index-concurrently-upsert-predicate_1.out deleted file mode 100644 index e72848d6a78..00000000000 --- a/src/test/modules/injection_points/expected/index-concurrently-upsert-predicate_1.out +++ /dev/null @@ -1,124 +0,0 @@ -Parsed test spec with 5 sessions - -starting permutation: s1_attach_invalidate_catalog_snapshot s4_wakeup_s1_setup s3_start_create_index s1_start_upsert s4_wakeup_define_index_before_set_valid s2_start_upsert s5_wakeup_s1_from_invalidate_catalog_snapshot s4_wakeup_s2 s4_wakeup_s1 -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -step s1_attach_invalidate_catalog_snapshot: - SELECT injection_points_attach('invalidate-catalog-snapshot-end', 'wait'); - <waiting ...> -step s4_wakeup_s1_setup: - SELECT CASE WHEN - (SELECT pid FROM pg_stat_activity - WHERE wait_event_type = 'InjectionPoint' AND - wait_event = 'invalidate-catalog-snapshot-end') IS NOT NULL - THEN injection_points_wakeup('invalidate-catalog-snapshot-end') - END; - -case ----- - -(1 row) - -step s1_attach_invalidate_catalog_snapshot: <... completed> -injection_points_attach ------------------------ - -(1 row) - -step s3_start_create_index: - CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_special_duplicate ON test.tbl(abs(i)) WHERE i < 10000; - <waiting ...> -step s1_start_upsert: - INSERT INTO test.tbl VALUES(13,now()) ON CONFLICT (abs(i)) WHERE i < 100 DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_define_index_before_set_valid: - SELECT injection_points_detach('define-index-before-set-valid'); - SELECT injection_points_wakeup('define-index-before-set-valid'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s2_start_upsert: - INSERT INTO test.tbl VALUES(13,now()) ON CONFLICT (abs(i)) WHERE i < 100 DO UPDATE SET updated_at = now(); - <waiting ...> -step s5_wakeup_s1_from_invalidate_catalog_snapshot: - DO $$ - DECLARE - v_waiting_pid INTEGER; - BEGIN - LOOP - SELECT pid INTO v_waiting_pid - FROM pg_stat_activity - WHERE wait_event_type = 'InjectionPoint' - AND wait_event = 'invalidate-catalog-snapshot-end' - LIMIT 1; - EXIT WHEN v_waiting_pid IS NOT NULL; - PERFORM pg_sleep(100); - END LOOP; - END - $$; - - SELECT injection_points_detach('invalidate-catalog-snapshot-end'); - SELECT injection_points_wakeup('invalidate-catalog-snapshot-end'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s4_wakeup_s2: - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s4_wakeup_s1: - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s1_start_upsert: <... completed> -step s2_start_upsert: <... completed> -step s3_start_create_index: <... completed> diff --git a/src/test/modules/injection_points/expected/index-concurrently-upsert.out b/src/test/modules/injection_points/expected/index-concurrently-upsert.out deleted file mode 100644 index a2ef122625c..00000000000 --- a/src/test/modules/injection_points/expected/index-concurrently-upsert.out +++ /dev/null @@ -1,123 +0,0 @@ -Parsed test spec with 5 sessions - -starting permutation: s1_attach_invalidate_catalog_snapshot s4_wakeup_s1_setup s3_start_create_index s1_start_upsert s4_wakeup_define_index_before_set_valid s2_start_upsert s5_wakeup_s1_from_invalidate_catalog_snapshot s4_wakeup_s2 s4_wakeup_s1 -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -step s1_attach_invalidate_catalog_snapshot: - SELECT injection_points_attach('invalidate-catalog-snapshot-end', 'wait'); - -injection_points_attach ------------------------ - -(1 row) - -step s4_wakeup_s1_setup: - SELECT CASE WHEN - (SELECT pid FROM pg_stat_activity - WHERE wait_event_type = 'InjectionPoint' AND - wait_event = 'invalidate-catalog-snapshot-end') IS NOT NULL - THEN injection_points_wakeup('invalidate-catalog-snapshot-end') - END; - -case ----- - -(1 row) - -step s3_start_create_index: - CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_duplicate ON test.tbl(i); - <waiting ...> -step s1_start_upsert: - INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_define_index_before_set_valid: - SELECT injection_points_detach('define-index-before-set-valid'); - SELECT injection_points_wakeup('define-index-before-set-valid'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s2_start_upsert: - INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); - <waiting ...> -step s5_wakeup_s1_from_invalidate_catalog_snapshot: - DO $$ - DECLARE - v_waiting_pid INTEGER; - BEGIN - LOOP - SELECT pid INTO v_waiting_pid - FROM pg_stat_activity - WHERE wait_event_type = 'InjectionPoint' - AND wait_event = 'invalidate-catalog-snapshot-end' - LIMIT 1; - EXIT WHEN v_waiting_pid IS NOT NULL; - PERFORM pg_sleep(100); - END LOOP; - END - $$; - - SELECT injection_points_detach('invalidate-catalog-snapshot-end'); - SELECT injection_points_wakeup('invalidate-catalog-snapshot-end'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s4_wakeup_s2: - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s4_wakeup_s1: - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s1_start_upsert: <... completed> -step s2_start_upsert: <... completed> -step s3_start_create_index: <... completed> diff --git a/src/test/modules/injection_points/expected/index-concurrently-upsert_1.out b/src/test/modules/injection_points/expected/index-concurrently-upsert_1.out deleted file mode 100644 index ee3b6641b90..00000000000 --- a/src/test/modules/injection_points/expected/index-concurrently-upsert_1.out +++ /dev/null @@ -1,124 +0,0 @@ -Parsed test spec with 5 sessions - -starting permutation: s1_attach_invalidate_catalog_snapshot s4_wakeup_s1_setup s3_start_create_index s1_start_upsert s4_wakeup_define_index_before_set_valid s2_start_upsert s5_wakeup_s1_from_invalidate_catalog_snapshot s4_wakeup_s2 s4_wakeup_s1 -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -step s1_attach_invalidate_catalog_snapshot: - SELECT injection_points_attach('invalidate-catalog-snapshot-end', 'wait'); - <waiting ...> -step s4_wakeup_s1_setup: - SELECT CASE WHEN - (SELECT pid FROM pg_stat_activity - WHERE wait_event_type = 'InjectionPoint' AND - wait_event = 'invalidate-catalog-snapshot-end') IS NOT NULL - THEN injection_points_wakeup('invalidate-catalog-snapshot-end') - END; - -case ----- - -(1 row) - -step s1_attach_invalidate_catalog_snapshot: <... completed> -injection_points_attach ------------------------ - -(1 row) - -step s3_start_create_index: - CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_duplicate ON test.tbl(i); - <waiting ...> -step s1_start_upsert: - INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_define_index_before_set_valid: - SELECT injection_points_detach('define-index-before-set-valid'); - SELECT injection_points_wakeup('define-index-before-set-valid'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s2_start_upsert: - INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); - <waiting ...> -step s5_wakeup_s1_from_invalidate_catalog_snapshot: - DO $$ - DECLARE - v_waiting_pid INTEGER; - BEGIN - LOOP - SELECT pid INTO v_waiting_pid - FROM pg_stat_activity - WHERE wait_event_type = 'InjectionPoint' - AND wait_event = 'invalidate-catalog-snapshot-end' - LIMIT 1; - EXIT WHEN v_waiting_pid IS NOT NULL; - PERFORM pg_sleep(100); - END LOOP; - END - $$; - - SELECT injection_points_detach('invalidate-catalog-snapshot-end'); - SELECT injection_points_wakeup('invalidate-catalog-snapshot-end'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s4_wakeup_s2: - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s4_wakeup_s1: - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s1_start_upsert: <... completed> -step s2_start_upsert: <... completed> -step s3_start_create_index: <... completed> diff --git a/src/test/modules/injection_points/expected/reindex-concurrently-upsert-on-constraint.out b/src/test/modules/injection_points/expected/reindex-concurrently-upsert-on-constraint.out deleted file mode 100644 index c1ac1f77c61..00000000000 --- a/src/test/modules/injection_points/expected/reindex-concurrently-upsert-on-constraint.out +++ /dev/null @@ -1,238 +0,0 @@ -Parsed test spec with 4 sessions - -starting permutation: s3_setup_wait_before_set_dead s3_start_reindex s1_start_upsert s4_wakeup_to_set_dead s2_start_upsert s4_wakeup_s1 s4_wakeup_s2 -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -injection_points_set_local --------------------------- - -(1 row) - -step s3_setup_wait_before_set_dead: - SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait'); - -injection_points_attach ------------------------ - -(1 row) - -step s3_start_reindex: - REINDEX INDEX CONCURRENTLY test.tbl_pkey; - <waiting ...> -step s1_start_upsert: - INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_to_set_dead: - SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead'); - SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s2_start_upsert: - INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_s1: - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s1_start_upsert: <... completed> -step s4_wakeup_s2: - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s2_start_upsert: <... completed> -step s3_start_reindex: <... completed> - -starting permutation: s3_setup_wait_before_swap s3_start_reindex s1_start_upsert s4_wakeup_to_swap s2_start_upsert s4_wakeup_s2 s4_wakeup_s1 -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -injection_points_set_local --------------------------- - -(1 row) - -step s3_setup_wait_before_swap: - SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait'); - -injection_points_attach ------------------------ - -(1 row) - -step s3_start_reindex: - REINDEX INDEX CONCURRENTLY test.tbl_pkey; - <waiting ...> -step s1_start_upsert: - INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_to_swap: - SELECT injection_points_detach('reindex-relation-concurrently-before-swap'); - SELECT injection_points_wakeup('reindex-relation-concurrently-before-swap'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s2_start_upsert: - INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_s2: - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s4_wakeup_s1: - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s1_start_upsert: <... completed> -step s2_start_upsert: <... completed> -step s3_start_reindex: <... completed> - -starting permutation: s3_setup_wait_before_set_dead s3_start_reindex s1_start_upsert s2_start_upsert s4_wakeup_s1 s4_wakeup_to_set_dead s4_wakeup_s2 -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -injection_points_set_local --------------------------- - -(1 row) - -step s3_setup_wait_before_set_dead: - SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait'); - -injection_points_attach ------------------------ - -(1 row) - -step s3_start_reindex: - REINDEX INDEX CONCURRENTLY test.tbl_pkey; - <waiting ...> -step s1_start_upsert: - INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now(); - <waiting ...> -step s2_start_upsert: - INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_s1: - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s1_start_upsert: <... completed> -step s4_wakeup_to_set_dead: - SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead'); - SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s4_wakeup_s2: - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s2_start_upsert: <... completed> -step s3_start_reindex: <... completed> diff --git a/src/test/modules/injection_points/expected/reindex-concurrently-upsert-partitioned.out b/src/test/modules/injection_points/expected/reindex-concurrently-upsert-partitioned.out deleted file mode 100644 index 4c79a43d986..00000000000 --- a/src/test/modules/injection_points/expected/reindex-concurrently-upsert-partitioned.out +++ /dev/null @@ -1,238 +0,0 @@ -Parsed test spec with 4 sessions - -starting permutation: s3_setup_wait_before_set_dead s3_start_reindex s1_start_upsert s4_wakeup_to_set_dead s2_start_upsert s4_wakeup_s1 s4_wakeup_s2 -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -injection_points_set_local --------------------------- - -(1 row) - -step s3_setup_wait_before_set_dead: - SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait'); - -injection_points_attach ------------------------ - -(1 row) - -step s3_start_reindex: - REINDEX INDEX CONCURRENTLY test.tbl_partition_pkey; - <waiting ...> -step s1_start_upsert: - INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_to_set_dead: - SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead'); - SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s2_start_upsert: - INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_s1: - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s1_start_upsert: <... completed> -step s4_wakeup_s2: - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s2_start_upsert: <... completed> -step s3_start_reindex: <... completed> - -starting permutation: s3_setup_wait_before_swap s3_start_reindex s1_start_upsert s4_wakeup_to_swap s2_start_upsert s4_wakeup_s2 s4_wakeup_s1 -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -injection_points_set_local --------------------------- - -(1 row) - -step s3_setup_wait_before_swap: - SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait'); - -injection_points_attach ------------------------ - -(1 row) - -step s3_start_reindex: - REINDEX INDEX CONCURRENTLY test.tbl_partition_pkey; - <waiting ...> -step s1_start_upsert: - INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_to_swap: - SELECT injection_points_detach('reindex-relation-concurrently-before-swap'); - SELECT injection_points_wakeup('reindex-relation-concurrently-before-swap'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s2_start_upsert: - INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_s2: - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s4_wakeup_s1: - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s1_start_upsert: <... completed> -step s2_start_upsert: <... completed> -step s3_start_reindex: <... completed> - -starting permutation: s3_setup_wait_before_set_dead s3_start_reindex s1_start_upsert s2_start_upsert s4_wakeup_s1 s4_wakeup_to_set_dead s4_wakeup_s2 -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -injection_points_set_local --------------------------- - -(1 row) - -step s3_setup_wait_before_set_dead: - SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait'); - -injection_points_attach ------------------------ - -(1 row) - -step s3_start_reindex: - REINDEX INDEX CONCURRENTLY test.tbl_partition_pkey; - <waiting ...> -step s1_start_upsert: - INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); - <waiting ...> -step s2_start_upsert: - INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_s1: - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s1_start_upsert: <... completed> -step s4_wakeup_to_set_dead: - SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead'); - SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s4_wakeup_s2: - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s2_start_upsert: <... completed> -step s3_start_reindex: <... completed> diff --git a/src/test/modules/injection_points/expected/reindex-concurrently-upsert.out b/src/test/modules/injection_points/expected/reindex-concurrently-upsert.out deleted file mode 100644 index c9cc9989d02..00000000000 --- a/src/test/modules/injection_points/expected/reindex-concurrently-upsert.out +++ /dev/null @@ -1,238 +0,0 @@ -Parsed test spec with 4 sessions - -starting permutation: s3_setup_wait_before_set_dead s3_start_reindex s1_start_upsert s4_wakeup_to_set_dead s2_start_upsert s4_wakeup_s1 s4_wakeup_s2 -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -injection_points_set_local --------------------------- - -(1 row) - -step s3_setup_wait_before_set_dead: - SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait'); - -injection_points_attach ------------------------ - -(1 row) - -step s3_start_reindex: - REINDEX INDEX CONCURRENTLY test.tbl_pkey; - <waiting ...> -step s1_start_upsert: - INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_to_set_dead: - SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead'); - SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s2_start_upsert: - INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_s1: - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s1_start_upsert: <... completed> -step s4_wakeup_s2: - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s2_start_upsert: <... completed> -step s3_start_reindex: <... completed> - -starting permutation: s3_setup_wait_before_swap s3_start_reindex s1_start_upsert s4_wakeup_to_swap s2_start_upsert s4_wakeup_s2 s4_wakeup_s1 -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -injection_points_set_local --------------------------- - -(1 row) - -step s3_setup_wait_before_swap: - SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait'); - -injection_points_attach ------------------------ - -(1 row) - -step s3_start_reindex: - REINDEX INDEX CONCURRENTLY test.tbl_pkey; - <waiting ...> -step s1_start_upsert: - INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_to_swap: - SELECT injection_points_detach('reindex-relation-concurrently-before-swap'); - SELECT injection_points_wakeup('reindex-relation-concurrently-before-swap'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s2_start_upsert: - INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_s2: - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s4_wakeup_s1: - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s1_start_upsert: <... completed> -step s2_start_upsert: <... completed> -step s3_start_reindex: <... completed> - -starting permutation: s3_setup_wait_before_set_dead s3_start_reindex s1_start_upsert s2_start_upsert s4_wakeup_s1 s4_wakeup_to_set_dead s4_wakeup_s2 -injection_points_attach ------------------------ - -(1 row) - -injection_points_attach ------------------------ - -(1 row) - -injection_points_set_local --------------------------- - -(1 row) - -step s3_setup_wait_before_set_dead: - SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait'); - -injection_points_attach ------------------------ - -(1 row) - -step s3_start_reindex: - REINDEX INDEX CONCURRENTLY test.tbl_pkey; - <waiting ...> -step s1_start_upsert: - INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); - <waiting ...> -step s2_start_upsert: - INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); - <waiting ...> -step s4_wakeup_s1: - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s1_start_upsert: <... completed> -step s4_wakeup_to_set_dead: - SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead'); - SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s4_wakeup_s2: - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); - -injection_points_detach ------------------------ - -(1 row) - -injection_points_wakeup ------------------------ - -(1 row) - -step s2_start_upsert: <... completed> -step s3_start_reindex: <... completed> diff --git a/src/test/modules/injection_points/meson.build b/src/test/modules/injection_points/meson.build index 493e11053dc..2c6cf54a33e 100644 --- a/src/test/modules/injection_points/meson.build +++ b/src/test/modules/injection_points/meson.build @@ -46,12 +46,6 @@ tests += { 'basic', 'inplace', 'syscache-update-pruned', - # temporarily disabled because of flakiness - # 'index-concurrently-upsert', - # 'index-concurrently-upsert-predicate', - # 'reindex-concurrently-upsert', - # 'reindex-concurrently-upsert-on-constraint', - # 'reindex-concurrently-upsert-partitioned', ], 'runningcheck': false, # see syscache-update-pruned # Some tests wait for all snapshots, so avoid parallel execution diff --git a/src/test/modules/injection_points/specs/index-concurrently-upsert-predicate.spec b/src/test/modules/injection_points/specs/index-concurrently-upsert-predicate.spec deleted file mode 100644 index d9b8d27fd1f..00000000000 --- a/src/test/modules/injection_points/specs/index-concurrently-upsert-predicate.spec +++ /dev/null @@ -1,124 +0,0 @@ -# This test verifies INSERT ON CONFLICT DO UPDATE behavior concurrent with -# CREATE INDEX CONCURRENTLY a partial index. -# -# - s1: UPSERT a tuple -# - s2: UPSERT the same tuple -# - s3: CREATE UNIQUE INDEX CONCURRENTLY (with a predicate) -# -# - s4 and s5: control concurrency via injection points - -setup -{ - CREATE EXTENSION injection_points; - CREATE SCHEMA test; - CREATE UNLOGGED TABLE test.tbl(i int, updated_at timestamp); - CREATE UNIQUE INDEX tbl_pkey_special ON test.tbl(abs(i)) WHERE i < 1000; - ALTER TABLE test.tbl SET (parallel_workers=0); -} - -teardown -{ - DROP SCHEMA test CASCADE; - DROP EXTENSION injection_points; -} - -session s1 -setup -{ - SELECT injection_points_set_local(); - SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait'); -} -step s1_attach_invalidate_catalog_snapshot -{ - SELECT injection_points_attach('invalidate-catalog-snapshot-end', 'wait'); -} -step s1_start_upsert -{ - INSERT INTO test.tbl VALUES(13,now()) ON CONFLICT (abs(i)) WHERE i < 100 DO UPDATE SET updated_at = now(); -} - -session s2 -setup -{ - SELECT injection_points_set_local(); - SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait'); -} -step s2_start_upsert -{ - INSERT INTO test.tbl VALUES(13,now()) ON CONFLICT (abs(i)) WHERE i < 100 DO UPDATE SET updated_at = now(); -} - -session s3 -setup -{ - SELECT injection_points_set_local(); - SELECT injection_points_attach('define-index-before-set-valid', 'wait'); -} -step s3_start_create_index -{ - CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_special_duplicate ON test.tbl(abs(i)) WHERE i < 10000; -} - -session s4 -# Step s1_attach_invalidate_catalog_snapshot sleeps or not depending on -# build conditions (CATCACHE_FORCE_RELEASE). Here we send a wakeup signal if -# it's sleeping or do nothing otherwise, and print a null value in either -# case. -step s4_wakeup_s1_setup -{ - SELECT CASE WHEN - (SELECT pid FROM pg_stat_activity - WHERE wait_event_type = 'InjectionPoint' AND - wait_event = 'invalidate-catalog-snapshot-end') IS NOT NULL - THEN injection_points_wakeup('invalidate-catalog-snapshot-end') - END; -} -step s4_wakeup_s1 -{ - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); -} -step s4_wakeup_s2 -{ - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); -} -step s4_wakeup_define_index_before_set_valid -{ - SELECT injection_points_detach('define-index-before-set-valid'); - SELECT injection_points_wakeup('define-index-before-set-valid'); -} - -session s5 -step s5_wakeup_s1_from_invalidate_catalog_snapshot -{ - DO $$ - DECLARE - v_waiting_pid INTEGER; - BEGIN - LOOP - SELECT pid INTO v_waiting_pid - FROM pg_stat_activity - WHERE wait_event_type = 'InjectionPoint' - AND wait_event = 'invalidate-catalog-snapshot-end' - LIMIT 1; - EXIT WHEN v_waiting_pid IS NOT NULL; - PERFORM pg_sleep(100); - END LOOP; - END - $$; - - SELECT injection_points_detach('invalidate-catalog-snapshot-end'); - SELECT injection_points_wakeup('invalidate-catalog-snapshot-end'); -} - -permutation - s1_attach_invalidate_catalog_snapshot - s4_wakeup_s1_setup - s3_start_create_index(s1_start_upsert, s2_start_upsert) - s1_start_upsert - s4_wakeup_define_index_before_set_valid - s2_start_upsert(s1_start_upsert) - s5_wakeup_s1_from_invalidate_catalog_snapshot - s4_wakeup_s2 - s4_wakeup_s1 diff --git a/src/test/modules/injection_points/specs/index-concurrently-upsert.spec b/src/test/modules/injection_points/specs/index-concurrently-upsert.spec deleted file mode 100644 index 6e08af74a93..00000000000 --- a/src/test/modules/injection_points/specs/index-concurrently-upsert.spec +++ /dev/null @@ -1,123 +0,0 @@ -# This test verifies INSERT ON CONFLICT DO UPDATE behavior concurrent with -# CREATE INDEX CONCURRENTLY. -# -# - s1: UPSERT a tuple -# - s2: UPSERT the same tuple -# - s3: CREATE UNIQUE INDEX CONCURRENTLY -# -# - s4: Control concurrency using injection points - -setup -{ - CREATE EXTENSION injection_points; - CREATE SCHEMA test; - CREATE UNLOGGED TABLE test.tbl(i int primary key, updated_at timestamp); - ALTER TABLE test.tbl SET (parallel_workers=0); -} - -teardown -{ - DROP SCHEMA test CASCADE; - DROP EXTENSION injection_points; -} - -session s1 -setup -{ - SELECT injection_points_set_local(); - SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait'); -} -step s1_attach_invalidate_catalog_snapshot -{ - SELECT injection_points_attach('invalidate-catalog-snapshot-end', 'wait'); -} -step s1_start_upsert -{ - INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); -} - -session s2 -setup -{ - SELECT injection_points_set_local(); - SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait'); -} -step s2_start_upsert -{ - INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); -} - -session s3 -setup -{ - SELECT injection_points_set_local(); - SELECT injection_points_attach('define-index-before-set-valid', 'wait'); -} -step s3_start_create_index -{ - CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_duplicate ON test.tbl(i); -} - -session s4 -# Step s1_attach_invalidate_catalog_snapshot sleeps or not depending on -# build conditions (CATCACHE_FORCE_RELEASE). Here we send a wakeup signal if -# it's sleeping or do nothing otherwise, and print a null value in either -# case. -step s4_wakeup_s1_setup -{ - SELECT CASE WHEN - (SELECT pid FROM pg_stat_activity - WHERE wait_event_type = 'InjectionPoint' AND - wait_event = 'invalidate-catalog-snapshot-end') IS NOT NULL - THEN injection_points_wakeup('invalidate-catalog-snapshot-end') - END; -} -step s4_wakeup_s1 -{ - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); -} -step s4_wakeup_s2 -{ - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); -} -step s4_wakeup_define_index_before_set_valid -{ - SELECT injection_points_detach('define-index-before-set-valid'); - SELECT injection_points_wakeup('define-index-before-set-valid'); -} - -session s5 -step s5_wakeup_s1_from_invalidate_catalog_snapshot -{ - DO $$ - DECLARE - v_waiting_pid INTEGER; - BEGIN - LOOP - SELECT pid INTO v_waiting_pid - FROM pg_stat_activity - WHERE wait_event_type = 'InjectionPoint' - AND wait_event = 'invalidate-catalog-snapshot-end' - LIMIT 1; - EXIT WHEN v_waiting_pid IS NOT NULL; - PERFORM pg_sleep(100); - END LOOP; - END - $$; - - SELECT injection_points_detach('invalidate-catalog-snapshot-end'); - SELECT injection_points_wakeup('invalidate-catalog-snapshot-end'); -} - -permutation - s1_attach_invalidate_catalog_snapshot - s4_wakeup_s1_setup - s3_start_create_index(s1_start_upsert, s2_start_upsert) - s1_start_upsert - s4_wakeup_define_index_before_set_valid - s2_start_upsert(s1_start_upsert) - s5_wakeup_s1_from_invalidate_catalog_snapshot - s4_wakeup_s2 - s4_wakeup_s1 diff --git a/src/test/modules/injection_points/specs/reindex-concurrently-upsert-on-constraint.spec b/src/test/modules/injection_points/specs/reindex-concurrently-upsert-on-constraint.spec deleted file mode 100644 index 4bbdda3cf04..00000000000 --- a/src/test/modules/injection_points/specs/reindex-concurrently-upsert-on-constraint.spec +++ /dev/null @@ -1,110 +0,0 @@ -# Test race conditions involving: -# -# - s1: UPSERT a tuple -# - s2: UPSERT the same tuple -# - s3: concurrently REINDEX the primary key -# -# - s4: operations with injection points - -setup -{ - CREATE EXTENSION injection_points; - CREATE SCHEMA test; - CREATE UNLOGGED TABLE test.tbl(i int primary key, updated_at timestamp); - ALTER TABLE test.tbl SET (parallel_workers=0); -} - -teardown -{ - DROP SCHEMA test CASCADE; - DROP EXTENSION injection_points; -} - -session s1 -setup -{ - SELECT injection_points_set_local(); - SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait'); -} -step s1_start_upsert -{ - INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now(); -} - -session s2 -setup -{ - SELECT injection_points_set_local(); - SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait'); -} -step s2_start_upsert -{ - INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now(); -} - -session s3 -setup -{ - SELECT injection_points_set_local(); -} -step s3_setup_wait_before_set_dead -{ - SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait'); -} -step s3_setup_wait_before_swap -{ - SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait'); -} -step s3_start_reindex -{ - REINDEX INDEX CONCURRENTLY test.tbl_pkey; -} - -session s4 -step s4_wakeup_to_swap -{ - SELECT injection_points_detach('reindex-relation-concurrently-before-swap'); - SELECT injection_points_wakeup('reindex-relation-concurrently-before-swap'); -} -step s4_wakeup_s1 -{ - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); -} -step s4_wakeup_s2 -{ - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); -} -step s4_wakeup_to_set_dead -{ - SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead'); - SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead'); -} - -permutation - s3_setup_wait_before_set_dead - s3_start_reindex(s1_start_upsert, s2_start_upsert) - s1_start_upsert(s4_wakeup_s2) - s4_wakeup_to_set_dead - s2_start_upsert(s1_start_upsert) - s4_wakeup_s1 - s4_wakeup_s2 - -permutation - s3_setup_wait_before_swap - s3_start_reindex(s1_start_upsert, s2_start_upsert) - s1_start_upsert(s4_wakeup_s2) - s4_wakeup_to_swap - s2_start_upsert(s1_start_upsert) - s4_wakeup_s2 - s4_wakeup_s1 - -permutation - s3_setup_wait_before_set_dead - s3_start_reindex(s1_start_upsert, s2_start_upsert) - s1_start_upsert(s4_wakeup_s2) - s2_start_upsert(s1_start_upsert) - s4_wakeup_s1 - s4_wakeup_to_set_dead - s4_wakeup_s2 diff --git a/src/test/modules/injection_points/specs/reindex-concurrently-upsert-partitioned.spec b/src/test/modules/injection_points/specs/reindex-concurrently-upsert-partitioned.spec deleted file mode 100644 index c3504b9ef38..00000000000 --- a/src/test/modules/injection_points/specs/reindex-concurrently-upsert-partitioned.spec +++ /dev/null @@ -1,113 +0,0 @@ -# This test verifies INSERT ON CONFLICT DO UPDATE behavior on partitioned -# tables concurrent with REINDEX CONCURRENTLY. -# -# - s1: UPSERT a tuple -# - s2: UPSERT the same tuple -# - s3: concurrently REINDEX the primary key index -# -# - s4: controls concurrency via injection points - -setup -{ - CREATE EXTENSION injection_points; - CREATE SCHEMA test; - CREATE TABLE test.tbl(i int primary key, updated_at timestamp) PARTITION BY RANGE (i); - CREATE TABLE test.tbl_partition PARTITION OF test.tbl - FOR VALUES FROM (0) TO (10000) - WITH (parallel_workers = 0); -} - -teardown -{ - DROP SCHEMA test CASCADE; - DROP EXTENSION injection_points; -} - -session s1 -setup -{ - SELECT injection_points_set_local(); - SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait'); -} -step s1_start_upsert -{ - INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); -} - -session s2 -setup -{ - SELECT injection_points_set_local(); - SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait'); -} -step s2_start_upsert -{ - INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); -} - -session s3 -setup -{ - SELECT injection_points_set_local(); -} -step s3_setup_wait_before_set_dead -{ - SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait'); -} -step s3_setup_wait_before_swap -{ - SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait'); -} -step s3_start_reindex -{ - REINDEX INDEX CONCURRENTLY test.tbl_partition_pkey; -} - -session s4 -step s4_wakeup_to_swap -{ - SELECT injection_points_detach('reindex-relation-concurrently-before-swap'); - SELECT injection_points_wakeup('reindex-relation-concurrently-before-swap'); -} -step s4_wakeup_s1 -{ - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); -} -step s4_wakeup_s2 -{ - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); -} -step s4_wakeup_to_set_dead -{ - SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead'); - SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead'); -} - -permutation - s3_setup_wait_before_set_dead - s3_start_reindex(s1_start_upsert, s2_start_upsert) - s1_start_upsert(s4_wakeup_s2) - s4_wakeup_to_set_dead - s2_start_upsert(s1_start_upsert) - s4_wakeup_s1 - s4_wakeup_s2 - -permutation - s3_setup_wait_before_swap - s3_start_reindex(s1_start_upsert, s2_start_upsert) - s1_start_upsert(s4_wakeup_s2) - s4_wakeup_to_swap - s2_start_upsert(s1_start_upsert) - s4_wakeup_s2 - s4_wakeup_s1 - -permutation - s3_setup_wait_before_set_dead - s3_start_reindex(s1_start_upsert, s2_start_upsert) - s1_start_upsert(s4_wakeup_s2) - s2_start_upsert(s1_start_upsert) - s4_wakeup_s1 - s4_wakeup_to_set_dead - s4_wakeup_s2 diff --git a/src/test/modules/injection_points/specs/reindex-concurrently-upsert.spec b/src/test/modules/injection_points/specs/reindex-concurrently-upsert.spec deleted file mode 100644 index 1b043a48ff4..00000000000 --- a/src/test/modules/injection_points/specs/reindex-concurrently-upsert.spec +++ /dev/null @@ -1,111 +0,0 @@ -# This test verifies INSERT ON CONFLICT DO UPDATE behavior concurrent with -# REINDEX CONCURRENTLY. -# -# - s1: UPSERT a tuple -# - s2: UPSERT the same tuple -# - s3: REINDEX concurrent primary key index -# -# - s4: controls concurrency via injection points - -setup -{ - CREATE EXTENSION injection_points; - CREATE SCHEMA test; - CREATE UNLOGGED TABLE test.tbl (i int PRIMARY KEY, updated_at timestamp); - ALTER TABLE test.tbl SET (parallel_workers=0); -} - -teardown -{ - DROP SCHEMA test CASCADE; - DROP EXTENSION injection_points; -} - -session s1 -setup -{ - SELECT injection_points_set_local(); - SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait'); -} -step s1_start_upsert -{ - INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); -} - -session s2 -setup -{ - SELECT injection_points_set_local(); - SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait'); -} -step s2_start_upsert -{ - INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); -} - -session s3 -setup -{ - SELECT injection_points_set_local(); -} -step s3_setup_wait_before_set_dead -{ - SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait'); -} -step s3_setup_wait_before_swap -{ - SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait'); -} -step s3_start_reindex -{ - REINDEX INDEX CONCURRENTLY test.tbl_pkey; -} - -session s4 -step s4_wakeup_to_swap -{ - SELECT injection_points_detach('reindex-relation-concurrently-before-swap'); - SELECT injection_points_wakeup('reindex-relation-concurrently-before-swap'); -} -step s4_wakeup_s1 -{ - SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict'); - SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict'); -} -step s4_wakeup_s2 -{ - SELECT injection_points_detach('exec-insert-before-insert-speculative'); - SELECT injection_points_wakeup('exec-insert-before-insert-speculative'); -} -step s4_wakeup_to_set_dead -{ - SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead'); - SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead'); -} - -permutation - s3_setup_wait_before_set_dead - s3_start_reindex(s1_start_upsert, s2_start_upsert) - s1_start_upsert(s4_wakeup_s2) - s4_wakeup_to_set_dead - s2_start_upsert(s1_start_upsert) - s4_wakeup_s1 - s4_wakeup_s2 - -permutation - s3_setup_wait_before_swap - s3_start_reindex(s1_start_upsert, s2_start_upsert) - s1_start_upsert(s4_wakeup_s2) - s4_wakeup_to_swap - s2_start_upsert(s1_start_upsert) - s4_wakeup_s2 - s4_wakeup_s1 - -permutation - s3_setup_wait_before_set_dead - s3_start_reindex(s1_start_upsert, s2_start_upsert) - s1_start_upsert(s4_wakeup_s2) - s2_start_upsert(s1_start_upsert) - s4_wakeup_s1 - s4_wakeup_to_set_dead - s4_wakeup_s2 diff --git a/src/test/modules/test_misc/Makefile b/src/test/modules/test_misc/Makefile index 399b9094a38..fedbef071ef 100644 --- a/src/test/modules/test_misc/Makefile +++ b/src/test/modules/test_misc/Makefile @@ -5,6 +5,9 @@ TAP_TESTS = 1 EXTRA_INSTALL=src/test/modules/injection_points \ contrib/test_decoding +# The injection points are cluster-wide, so disable installcheck +NO_INSTALLCHECK = 1 + export enable_injection_points ifdef USE_PGXS diff --git a/src/test/modules/test_misc/meson.build b/src/test/modules/test_misc/meson.build index f258bf1ccd9..129c4ae587a 100644 --- a/src/test/modules/test_misc/meson.build +++ b/src/test/modules/test_misc/meson.build @@ -18,6 +18,9 @@ tests += { 't/007_catcache_inval.pl', 't/008_replslot_single_user.pl', 't/009_log_temp_files.pl', + 't/010_index_concurrently_upsert.pl', ], + # The injection points are cluster-wide, so disable installcheck + 'runningcheck': false, }, } diff --git a/src/test/modules/test_misc/t/010_index_concurrently_upsert.pl b/src/test/modules/test_misc/t/010_index_concurrently_upsert.pl new file mode 100644 index 00000000000..55ea384edb3 --- /dev/null +++ b/src/test/modules/test_misc/t/010_index_concurrently_upsert.pl @@ -0,0 +1,894 @@ + +# Copyright (c) 2025, PostgreSQL Global Development Group + +# Test INSERT ON CONFLICT DO UPDATE behavior concurrent with +# CREATE INDEX CONCURRENTLY and REINDEX CONCURRENTLY. +# +# These tests verify the fix for "duplicate key value violates unique constraint" +# errors that occurred when infer_arbiter_indexes() only considered indisvalid +# indexes, causing different transactions to use different arbiter indexes. + +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +if ($ENV{enable_injection_points} ne 'yes') +{ + plan skip_all => 'Injection points not supported by this build'; +} + +# Node initialization +my $node = PostgreSQL::Test::Cluster->new('node'); +$node->init(); +$node->start; + +# Check if the extension injection_points is available +if (!$node->check_extension('injection_points')) +{ + plan skip_all => 'Extension injection_points not installed'; +} + +$node->safe_psql('postgres', 'CREATE EXTENSION injection_points;'); + +# Helper: Wait for a session to hit an injection point. +# Optional second argument is timeout in seconds. +# Returns true if found, false if timeout. +# On timeout, logs diagnostic information about all active queries. +sub wait_for_injection_point +{ + my ($node, $point_name, $timeout) = @_; + $timeout //= 120; + + for (my $elapsed = 0; $elapsed < $timeout; $elapsed++) + { + my $pid = $node->safe_psql('postgres', qq[ + SELECT pid FROM pg_stat_activity + WHERE wait_event_type = 'InjectionPoint' + AND wait_event = '$point_name' + LIMIT 1; + ]); + return 1 if $pid ne ''; + sleep(1); + } + + # Timeout - report diagnostic information + my $activity = $node->safe_psql('postgres', q[ + SELECT format('pid=%s, state=%s, wait_event_type=%s, wait_event=%s, backend_xmin=%s, backend_xid=%s, query=%s', + pid, state, wait_event_type, wait_event, backend_xmin, backend_xid, left(query, 100)) + FROM pg_stat_activity + ORDER BY pid; + ]); + diag("wait_for_injection_point timeout waiting for: $point_name\n" . + "Current queries in pg_stat_activity:\n$activity"); + + return 0; +} + +# Helper: Wait for a specific backend to become idle. +# Returns true if idle, false if timeout. +sub wait_for_idle +{ + my ($node, $pid, $timeout) = @_; + $timeout //= 15; + + for (my $elapsed = 0; $elapsed < $timeout; $elapsed++) + { + my $state = $node->safe_psql('postgres', qq[ + SELECT state FROM pg_stat_activity WHERE pid = $pid; + ]); + return 1 if $state eq 'idle'; + sleep(1); + } + return 0; +} + +# Helper: Detach and wakeup an injection point +sub wakeup_injection_point +{ + my ($node, $point_name) = @_; + $node->safe_psql( + 'postgres', qq[ +SELECT injection_points_detach('$point_name'); +SELECT injection_points_wakeup('$point_name'); +]); +} + +# Wait for any pending query to complete, capture stderr, and close the session. +# Returns the stderr output (excluding internal markers). +sub safe_quit +{ + my ($session) = @_; + + # Send a marker and wait for it to ensure any pending query completes + my $banner = "safe_quit_marker"; + my $banner_match = qr/(^|\n)$banner\r?\n/; + + $session->{stdin} .= "\\echo $banner\n\\warn $banner\n"; + + pump_until($session->{run}, $session->{timeout}, + \$session->{stdout}, $banner_match); + pump_until($session->{run}, $session->{timeout}, + \$session->{stderr}, $banner_match); + + # Capture stderr (excluding the banner) + my $stderr = $session->{stderr}; + $stderr =~ s/$banner_match//; + + # Close the session + $session->quit; + + return $stderr; +} + +############################################################################### +# Test 1: REINDEX CONCURRENTLY + UPSERT (wakeup at set-dead phase) +# Based on reindex-concurrently-upsert.spec +############################################################################### + +$node->safe_psql( + 'postgres', q[ +CREATE SCHEMA test; +CREATE UNLOGGED TABLE test.tbl (i int PRIMARY KEY, updated_at timestamp); +ALTER TABLE test.tbl SET (parallel_workers=0); +]); + +# Create sessions with on_error_stop => 0 so psql doesn't exit on SQL errors. +# This allows us to collect stderr and detect errors after the test completes. +my $s1 = $node->background_psql('postgres', on_error_stop => 0); +my $s2 = $node->background_psql('postgres', on_error_stop => 0); +my $s3 = $node->background_psql('postgres', on_error_stop => 0); + +# Setup injection points for each session +$s1->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait'); +]); + +$s2->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait'); +]); + +$s3->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait'); +]); + +# s3 starts REINDEX (will block on reindex-relation-concurrently-before-set-dead) +$s3->query_until(qr/starting_reindex/, q[ +\echo starting_reindex +REINDEX INDEX CONCURRENTLY test.tbl_pkey; +]); + +# Wait for s3 to hit injection point +ok(wait_for_injection_point($node, 'reindex-relation-concurrently-before-set-dead')); + +# s1 starts UPSERT (will block on check-exclusion-or-unique-constraint-no-conflict) +$s1->query_until(qr/starting_upsert_s1/, q[ +\echo starting_upsert_s1 +INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); +]); + +# Wait for s1 to hit injection point +ok(wait_for_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict')); + +# Wakeup s3 to continue (reindex-relation-concurrently-before-set-dead) +wakeup_injection_point($node, 'reindex-relation-concurrently-before-set-dead'); + +# s2 starts UPSERT (will block on exec-insert-before-insert-speculative) +$s2->query_until(qr/starting_upsert_s2/, q[ +\echo starting_upsert_s2 +INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); +]); + +# Wait for s2 to hit injection point +ok(wait_for_injection_point($node, 'exec-insert-before-insert-speculative')); + +# Wakeup s1 (check-exclusion-or-unique-constraint-no-conflict) +wakeup_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict'); + +# Wakeup s2 (exec-insert-before-insert-speculative) +wakeup_injection_point($node, 'exec-insert-before-insert-speculative'); + +is(safe_quit($s1), '', 'Test 1 (REINDEX swap): session s1 quit successfully'); +is(safe_quit($s2), '', 'Test 1 (REINDEX swap): session s2 quit successfully'); +is(safe_quit($s3), '', 'Test 1 (REINDEX swap): session s3 quit successfully'); + +# Cleanup test 1 +$node->safe_psql('postgres', 'DROP SCHEMA test CASCADE;'); + +############################################################################### +# Test 2: REINDEX CONCURRENTLY + UPSERT (wakeup at swap phase) +############################################################################### + +$node->safe_psql( + 'postgres', q[ +CREATE SCHEMA test; +CREATE UNLOGGED TABLE test.tbl (i int PRIMARY KEY, updated_at timestamp); +ALTER TABLE test.tbl SET (parallel_workers=0); +]); + +$s1 = $node->background_psql('postgres', on_error_stop => 0); +$s2 = $node->background_psql('postgres', on_error_stop => 0); +$s3 = $node->background_psql('postgres', on_error_stop => 0); + +$s1->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait'); +]); + +$s2->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait'); +]); + +$s3->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait'); +]); + +$s3->query_until(qr/starting_reindex/, q[ +\echo starting_reindex +REINDEX INDEX CONCURRENTLY test.tbl_pkey; +]); + +ok(wait_for_injection_point($node, 'reindex-relation-concurrently-before-swap')); + +$s1->query_until(qr/starting_upsert_s1/, q[ +\echo starting_upsert_s1 +INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict')); + +wakeup_injection_point($node, 'reindex-relation-concurrently-before-swap'); + +$s2->query_until(qr/starting_upsert_s2/, q[ +\echo starting_upsert_s2 +INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'exec-insert-before-insert-speculative')); + +wakeup_injection_point($node, 'exec-insert-before-insert-speculative'); +wakeup_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict'); + +is(safe_quit($s1), '', 'Test 2 (REINDEX set-dead): session s1 quit successfully'); +is(safe_quit($s2), '', 'Test 2 (REINDEX set-dead): session s2 quit successfully'); +is(safe_quit($s3), '', 'Test 2 (REINDEX set-dead): session s3 quit successfully'); + +$node->safe_psql('postgres', 'DROP SCHEMA test CASCADE;'); + +############################################################################### +# Test 2b: REINDEX CONCURRENTLY + UPSERT (permutation 3: s1 wakes before reindex) +# Different timing: s2 starts, then s1 wakes, then reindex wakes, then s2 wakes +############################################################################### + +$node->safe_psql( + 'postgres', q[ +CREATE SCHEMA test; +CREATE UNLOGGED TABLE test.tbl (i int PRIMARY KEY, updated_at timestamp); +ALTER TABLE test.tbl SET (parallel_workers=0); +]); + +$s1 = $node->background_psql('postgres', on_error_stop => 0); +$s2 = $node->background_psql('postgres', on_error_stop => 0); +$s3 = $node->background_psql('postgres', on_error_stop => 0); + +$s1->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait'); +]); + +$s2->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait'); +]); + +$s3->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait'); +]); + +$s3->query_until(qr/starting_reindex/, q[ +\echo starting_reindex +REINDEX INDEX CONCURRENTLY test.tbl_pkey; +]); + +ok(wait_for_injection_point($node, 'reindex-relation-concurrently-before-set-dead')); + +$s1->query_until(qr/starting_upsert_s1/, q[ +\echo starting_upsert_s1 +INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict')); + +# Start s2 BEFORE waking reindex (key difference from permutation 1) +$s2->query_until(qr/starting_upsert_s2/, q[ +\echo starting_upsert_s2 +INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'exec-insert-before-insert-speculative')); + +# Wake s1 first, then reindex, then s2 +wakeup_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict'); +wakeup_injection_point($node, 'reindex-relation-concurrently-before-set-dead'); +wakeup_injection_point($node, 'exec-insert-before-insert-speculative'); + +is(safe_quit($s1), '', 'Test 2b (REINDEX perm3): session s1 quit successfully'); +is(safe_quit($s2), '', 'Test 2b (REINDEX perm3): session s2 quit successfully'); +is(safe_quit($s3), '', 'Test 2b (REINDEX perm3): session s3 quit successfully'); + +$node->safe_psql('postgres', 'DROP SCHEMA test CASCADE;'); + +############################################################################### +# Test 3: REINDEX + UPSERT ON CONSTRAINT (set-dead phase) +# Based on reindex-concurrently-upsert-on-constraint.spec +############################################################################### + +$node->safe_psql( + 'postgres', q[ +CREATE SCHEMA test; +CREATE UNLOGGED TABLE test.tbl (i int PRIMARY KEY, updated_at timestamp); +ALTER TABLE test.tbl SET (parallel_workers=0); +]); + +$s1 = $node->background_psql('postgres', on_error_stop => 0); +$s2 = $node->background_psql('postgres', on_error_stop => 0); +$s3 = $node->background_psql('postgres', on_error_stop => 0); + +$s1->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait'); +]); + +$s2->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait'); +]); + +$s3->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait'); +]); + +$s3->query_until(qr/starting_reindex/, q[ +\echo starting_reindex +REINDEX INDEX CONCURRENTLY test.tbl_pkey; +]); + +ok(wait_for_injection_point($node, 'reindex-relation-concurrently-before-set-dead')); + +$s1->query_until(qr/starting_upsert_s1/, q[ +\echo starting_upsert_s1 +INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict')); + +wakeup_injection_point($node, 'reindex-relation-concurrently-before-set-dead'); + +$s2->query_until(qr/starting_upsert_s2/, q[ +\echo starting_upsert_s2 +INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'exec-insert-before-insert-speculative')); + +wakeup_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict'); +wakeup_injection_point($node, 'exec-insert-before-insert-speculative'); + +is(safe_quit($s1), '', 'Test 3 (ON CONSTRAINT set-dead): session s1 quit successfully'); +is(safe_quit($s2), '', 'Test 3 (ON CONSTRAINT set-dead): session s2 quit successfully'); +is(safe_quit($s3), '', 'Test 3 (ON CONSTRAINT set-dead): session s3 quit successfully'); + +$node->safe_psql('postgres', 'DROP SCHEMA test CASCADE;'); + +############################################################################### +# Test 4: REINDEX + UPSERT ON CONSTRAINT (swap phase) +############################################################################### + +$node->safe_psql( + 'postgres', q[ +CREATE SCHEMA test; +CREATE UNLOGGED TABLE test.tbl (i int PRIMARY KEY, updated_at timestamp); +ALTER TABLE test.tbl SET (parallel_workers=0); +]); + +$s1 = $node->background_psql('postgres', on_error_stop => 0); +$s2 = $node->background_psql('postgres', on_error_stop => 0); +$s3 = $node->background_psql('postgres', on_error_stop => 0); + +$s1->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait'); +]); + +$s2->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait'); +]); + +$s3->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait'); +]); + +$s3->query_until(qr/starting_reindex/, q[ +\echo starting_reindex +REINDEX INDEX CONCURRENTLY test.tbl_pkey; +]); + +ok(wait_for_injection_point($node, 'reindex-relation-concurrently-before-swap')); + +$s1->query_until(qr/starting_upsert_s1/, q[ +\echo starting_upsert_s1 +INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict')); + +wakeup_injection_point($node, 'reindex-relation-concurrently-before-swap'); + +$s2->query_until(qr/starting_upsert_s2/, q[ +\echo starting_upsert_s2 +INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'exec-insert-before-insert-speculative')); + +wakeup_injection_point($node, 'exec-insert-before-insert-speculative'); +wakeup_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict'); + +is(safe_quit($s1), '', 'Test 4 (ON CONSTRAINT swap): session s1 quit successfully'); +is(safe_quit($s2), '', 'Test 4 (ON CONSTRAINT swap): session s2 quit successfully'); +is(safe_quit($s3), '', 'Test 4 (ON CONSTRAINT swap): session s3 quit successfully'); + +$node->safe_psql('postgres', 'DROP SCHEMA test CASCADE;'); + +############################################################################### +# Test 4b: REINDEX + UPSERT ON CONSTRAINT (permutation 3: s1 wakes before reindex) +############################################################################### + +$node->safe_psql( + 'postgres', q[ +CREATE SCHEMA test; +CREATE UNLOGGED TABLE test.tbl (i int PRIMARY KEY, updated_at timestamp); +ALTER TABLE test.tbl SET (parallel_workers=0); +]); + +$s1 = $node->background_psql('postgres', on_error_stop => 0); +$s2 = $node->background_psql('postgres', on_error_stop => 0); +$s3 = $node->background_psql('postgres', on_error_stop => 0); + +$s1->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait'); +]); + +$s2->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait'); +]); + +$s3->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait'); +]); + +$s3->query_until(qr/starting_reindex/, q[ +\echo starting_reindex +REINDEX INDEX CONCURRENTLY test.tbl_pkey; +]); + +ok(wait_for_injection_point($node, 'reindex-relation-concurrently-before-set-dead')); + +$s1->query_until(qr/starting_upsert_s1/, q[ +\echo starting_upsert_s1 +INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict')); + +# Start s2 BEFORE waking reindex +$s2->query_until(qr/starting_upsert_s2/, q[ +\echo starting_upsert_s2 +INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'exec-insert-before-insert-speculative')); + +# Wake s1 first, then reindex, then s2 +wakeup_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict'); +wakeup_injection_point($node, 'reindex-relation-concurrently-before-set-dead'); +wakeup_injection_point($node, 'exec-insert-before-insert-speculative'); + +is(safe_quit($s1), '', 'Test 4b (ON CONSTRAINT perm3): session s1 quit successfully'); +is(safe_quit($s2), '', 'Test 4b (ON CONSTRAINT perm3): session s2 quit successfully'); +is(safe_quit($s3), '', 'Test 4b (ON CONSTRAINT perm3): session s3 quit successfully'); + +$node->safe_psql('postgres', 'DROP SCHEMA test CASCADE;'); + +############################################################################### +# Test 5: REINDEX on partitioned table (set-dead phase) +# Based on reindex-concurrently-upsert-partitioned.spec +############################################################################### + +$node->safe_psql( + 'postgres', q[ +CREATE SCHEMA test; +CREATE TABLE test.tbl(i int primary key, updated_at timestamp) PARTITION BY RANGE (i); +CREATE TABLE test.tbl_partition PARTITION OF test.tbl + FOR VALUES FROM (0) TO (10000) + WITH (parallel_workers = 0); +]); + +$s1 = $node->background_psql('postgres', on_error_stop => 0); +$s2 = $node->background_psql('postgres', on_error_stop => 0); +$s3 = $node->background_psql('postgres', on_error_stop => 0); + +$s1->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait'); +]); + +$s2->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait'); +]); + +$s3->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait'); +]); + +$s3->query_until(qr/starting_reindex/, q[ +\echo starting_reindex +REINDEX INDEX CONCURRENTLY test.tbl_partition_pkey; +]); + +ok(wait_for_injection_point($node, 'reindex-relation-concurrently-before-set-dead')); + +$s1->query_until(qr/starting_upsert_s1/, q[ +\echo starting_upsert_s1 +INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict')); + +wakeup_injection_point($node, 'reindex-relation-concurrently-before-set-dead'); + +$s2->query_until(qr/starting_upsert_s2/, q[ +\echo starting_upsert_s2 +INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'exec-insert-before-insert-speculative')); + +wakeup_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict'); +wakeup_injection_point($node, 'exec-insert-before-insert-speculative'); + +is(safe_quit($s1), '', 'Test 5 (partitioned set-dead): session s1 quit successfully'); +is(safe_quit($s2), '', 'Test 5 (partitioned set-dead): session s2 quit successfully'); +is(safe_quit($s3), '', 'Test 5 (partitioned set-dead): session s3 quit successfully'); + +$node->safe_psql('postgres', 'DROP SCHEMA test CASCADE;'); + +############################################################################### +# Test 6: REINDEX on partitioned table (swap phase) +############################################################################### + +$node->safe_psql( + 'postgres', q[ +CREATE SCHEMA test; +CREATE TABLE test.tbl(i int primary key, updated_at timestamp) PARTITION BY RANGE (i); +CREATE TABLE test.tbl_partition PARTITION OF test.tbl + FOR VALUES FROM (0) TO (10000) + WITH (parallel_workers = 0); +]); + +$s1 = $node->background_psql('postgres', on_error_stop => 0); +$s2 = $node->background_psql('postgres', on_error_stop => 0); +$s3 = $node->background_psql('postgres', on_error_stop => 0); + +$s1->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait'); +]); + +$s2->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait'); +]); + +$s3->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait'); +]); + +$s3->query_until(qr/starting_reindex/, q[ +\echo starting_reindex +REINDEX INDEX CONCURRENTLY test.tbl_partition_pkey; +]); + +ok(wait_for_injection_point($node, 'reindex-relation-concurrently-before-swap')); + +$s1->query_until(qr/starting_upsert_s1/, q[ +\echo starting_upsert_s1 +INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict')); + +wakeup_injection_point($node, 'reindex-relation-concurrently-before-swap'); + +$s2->query_until(qr/starting_upsert_s2/, q[ +\echo starting_upsert_s2 +INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'exec-insert-before-insert-speculative')); + +wakeup_injection_point($node, 'exec-insert-before-insert-speculative'); +wakeup_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict'); + +is(safe_quit($s1), '', 'Test 6 (partitioned swap): session s1 quit successfully'); +is(safe_quit($s2), '', 'Test 6 (partitioned swap): session s2 quit successfully'); +is(safe_quit($s3), '', 'Test 6 (partitioned swap): session s3 quit successfully'); + +$node->safe_psql('postgres', 'DROP SCHEMA test CASCADE;'); + +############################################################################### +# Test 6b: REINDEX on partitioned table (permutation 3: s1 wakes before reindex) +############################################################################### + +$node->safe_psql( + 'postgres', q[ +CREATE SCHEMA test; +CREATE TABLE test.tbl(i int primary key, updated_at timestamp) PARTITION BY RANGE (i); +CREATE TABLE test.tbl_partition PARTITION OF test.tbl + FOR VALUES FROM (0) TO (10000) + WITH (parallel_workers = 0); +]); + +$s1 = $node->background_psql('postgres', on_error_stop => 0); +$s2 = $node->background_psql('postgres', on_error_stop => 0); +$s3 = $node->background_psql('postgres', on_error_stop => 0); + +$s1->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait'); +]); + +$s2->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait'); +]); + +$s3->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait'); +]); + +$s3->query_until(qr/starting_reindex/, q[ +\echo starting_reindex +REINDEX INDEX CONCURRENTLY test.tbl_partition_pkey; +]); + +ok(wait_for_injection_point($node, 'reindex-relation-concurrently-before-set-dead')); + +$s1->query_until(qr/starting_upsert_s1/, q[ +\echo starting_upsert_s1 +INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict')); + +# Start s2 BEFORE waking reindex +$s2->query_until(qr/starting_upsert_s2/, q[ +\echo starting_upsert_s2 +INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'exec-insert-before-insert-speculative')); + +# Wake s1 first, then reindex, then s2 +wakeup_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict'); +wakeup_injection_point($node, 'reindex-relation-concurrently-before-set-dead'); +wakeup_injection_point($node, 'exec-insert-before-insert-speculative'); + +is(safe_quit($s1), '', 'Test 6b (partitioned perm3): session s1 quit successfully'); +is(safe_quit($s2), '', 'Test 6b (partitioned perm3): session s2 quit successfully'); +is(safe_quit($s3), '', 'Test 6b (partitioned perm3): session s3 quit successfully'); + +$node->safe_psql('postgres', 'DROP SCHEMA test CASCADE;'); + +############################################################################### +# Test 7: CREATE INDEX CONCURRENTLY + UPSERT +# Based on index-concurrently-upsert.spec +# Uses invalidate-catalog-snapshot-end to test catalog invalidation during UPSERT +############################################################################### + +$node->safe_psql( + 'postgres', q[ +CREATE SCHEMA test; +CREATE UNLOGGED TABLE test.tbl(i int primary key, updated_at timestamp); +ALTER TABLE test.tbl SET (parallel_workers=0); +]); + +$s1 = $node->background_psql('postgres', on_error_stop => 0); +$s2 = $node->background_psql('postgres', on_error_stop => 0); +$s3 = $node->background_psql('postgres', on_error_stop => 0); + +my $s1_pid = $s1->query_safe('SELECT pg_backend_pid()'); + +# s1 attaches BOTH injection points - the unique constraint check AND catalog snapshot +$s1->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait'); +]); + +$s1->query_until(qr/attaching_injection_point/, q[ +\echo attaching_injection_point +SELECT injection_points_attach('invalidate-catalog-snapshot-end', 'wait'); +]); +# In case of CLOBBER_CACHE_ALWAYS - s1 may hit the injection point during attach. +# Wait for s1 to become idle (attach completed) or wakeup if stuck on injection point. +if (!wait_for_idle($node, $s1_pid)) +{ + ok(wait_for_injection_point($node, 'invalidate-catalog-snapshot-end'), + 'Test 7: s1 hit injection point during attach (CLOBBER_CACHE_ALWAYS)'); + $node->safe_psql('postgres', q[ + SELECT injection_points_wakeup('invalidate-catalog-snapshot-end'); + ]); +} + +$s2->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait'); +]); + +$s3->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('define-index-before-set-valid', 'wait'); +]); + +# s3: Start CREATE INDEX CONCURRENTLY (blocks on define-index-before-set-valid) +$s3->query_until(qr/starting_create_index/, q[ +\echo starting_create_index +CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_duplicate ON test.tbl(i); +]); + +ok(wait_for_injection_point($node, 'define-index-before-set-valid')); + +# s1: Start UPSERT (blocks on invalidate-catalog-snapshot-end) +$s1->query_until(qr/starting_upsert_s1/, q[ +\echo starting_upsert_s1 +INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'invalidate-catalog-snapshot-end')); + +# Wakeup s3 (CREATE INDEX continues, triggers catalog invalidation) +wakeup_injection_point($node, 'define-index-before-set-valid'); + +# s2: Start UPSERT (blocks on exec-insert-before-insert-speculative) +$s2->query_until(qr/starting_upsert_s2/, q[ +\echo starting_upsert_s2 +INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'exec-insert-before-insert-speculative')); + +wakeup_injection_point($node, 'invalidate-catalog-snapshot-end'); + +ok(wait_for_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict')); + +wakeup_injection_point($node, 'exec-insert-before-insert-speculative'); + +wakeup_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict'); + +is(safe_quit($s1), '', 'Test 7 (CREATE INDEX): session s1 quit successfully'); +is(safe_quit($s2), '', 'Test 7 (CREATE INDEX): session s2 quit successfully'); +is(safe_quit($s3), '', 'Test 7 (CREATE INDEX): session s3 quit successfully'); + +$node->safe_psql('postgres', 'DROP SCHEMA test CASCADE;'); + +############################################################################### +# Test 8: CREATE INDEX CONCURRENTLY on partial index + UPSERT +# Based on index-concurrently-upsert-predicate.spec +# Uses invalidate-catalog-snapshot-end to test catalog invalidation during UPSERT +############################################################################### + +$node->safe_psql( + 'postgres', q[ +CREATE SCHEMA test; +CREATE UNLOGGED TABLE test.tbl(i int, updated_at timestamp); +CREATE UNIQUE INDEX tbl_pkey_special ON test.tbl(abs(i)) WHERE i < 1000; +ALTER TABLE test.tbl SET (parallel_workers=0); +]); + +$s1 = $node->background_psql('postgres', on_error_stop => 0); +$s2 = $node->background_psql('postgres', on_error_stop => 0); +$s3 = $node->background_psql('postgres', on_error_stop => 0); + +$s1_pid = $s1->query_safe('SELECT pg_backend_pid()'); + +# s1 attaches BOTH injection points - the unique constraint check AND catalog snapshot +$s1->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait'); +]); + +$s1->query_until(qr/attaching_injection_point/, q[ +\echo attaching_injection_point +SELECT injection_points_attach('invalidate-catalog-snapshot-end', 'wait'); +]); +# In case of CLOBBER_CACHE_ALWAYS - s1 may hit the injection point during attach. +# Wait for s1 to become idle (attach completed) or wakeup if stuck on injection point. +if (!wait_for_idle($node, $s1_pid)) +{ + ok(wait_for_injection_point($node, 'invalidate-catalog-snapshot-end'), + 'Test 8: s1 hit injection point during attach (CLOBBER_CACHE_ALWAYS)'); + $node->safe_psql('postgres', q[ + SELECT injection_points_wakeup('invalidate-catalog-snapshot-end'); + ]); +} + +$s2->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait'); +]); + +$s3->query_safe(q[ +SELECT injection_points_set_local(); +SELECT injection_points_attach('define-index-before-set-valid', 'wait'); +]); + +# s3: Start CREATE INDEX CONCURRENTLY (blocks on define-index-before-set-valid) +$s3->query_until(qr/starting_create_index/, q[ +\echo starting_create_index +CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_special_duplicate ON test.tbl(abs(i)) WHERE i < 10000; +]); + +ok(wait_for_injection_point($node, 'define-index-before-set-valid')); + +# s1: Start UPSERT (blocks on invalidate-catalog-snapshot-end) +$s1->query_until(qr/starting_upsert_s1/, q[ +\echo starting_upsert_s1 +INSERT INTO test.tbl VALUES(13,now()) ON CONFLICT (abs(i)) WHERE i < 100 DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'invalidate-catalog-snapshot-end')); + +# Wakeup s3 (CREATE INDEX continues, triggers catalog invalidation) +wakeup_injection_point($node, 'define-index-before-set-valid'); + +# s2: Start UPSERT (blocks on exec-insert-before-insert-speculative) +$s2->query_until(qr/starting_upsert_s2/, q[ +\echo starting_upsert_s2 +INSERT INTO test.tbl VALUES(13,now()) ON CONFLICT (abs(i)) WHERE i < 100 DO UPDATE SET updated_at = now(); +]); + +ok(wait_for_injection_point($node, 'exec-insert-before-insert-speculative')); + +wakeup_injection_point($node, 'invalidate-catalog-snapshot-end'); + +ok(wait_for_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict')); + +wakeup_injection_point($node, 'exec-insert-before-insert-speculative'); + +wakeup_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict'); + +is(safe_quit($s1), '', 'Test 7 (CREATE INDEX): session s1 quit successfully'); +is(safe_quit($s2), '', 'Test 7 (CREATE INDEX): session s2 quit successfully'); +is(safe_quit($s3), '', 'Test 7 (CREATE INDEX): session s3 quit successfully'); + +$node->safe_psql('postgres', 'DROP SCHEMA test CASCADE;'); + +done_testing(); -- 2.52.0
