From 7ed9e2409d6b0fc4e4a915ba45232a10326c778b Mon Sep 17 00:00:00 2001 From: nkey Date: Mon, 25 Nov 2024 19:13:40 +0100 Subject: [PATCH v1] meson test --print-errorlogs --num-processes=8 --setup running reproducer for strange assert: 2024-11-25 19:08:12.063 CET [562801][client backend] [isolation/two-ids/s2][50/131:536136] ERROR: could not serialize access due to read/write dependencies among transactions 2024-11-25 19:08:12.063 CET [562801][client backend] [isolation/two-ids/s2][50/131:536136] DETAIL: Reason code: Canceled on identification as a pivot, during commit attempt. 2024-11-25 19:08:12.063 CET [562801][client backend] [isolation/two-ids/s2][50/131:536136] HINT: The transaction might succeed if retried. 2024-11-25 19:08:12.063 CET [562801][client backend] [isolation/two-ids/s2][50/131:536136] STATEMENT: COMMIT; 2024-11-25 19:08:12.063 CET [562801][client backend] [isolation/two-ids/s2][50/0:536136] ERROR: ResourceOwnerEnlarge called after release started 2024-11-25 19:08:12.063 CET [562801][client backend] [isolation/two-ids/s2][50/0:536136] WARNING: AbortTransaction while in ABORT state TRAP: failed Assert("TransactionIdIsValid(proc->xid)"), File: "../src/backend/storage/ipc/procarray.c", Line: 677, PID: 562801 2024-11-25 19:08:12.064 CET [562819][client backend] [pg_regress/test_parser][:0] LOG: disconnection: session time: 0:00:00.011 user=nkey database=regression_test_parser host=[local] postgres: nkey isolation_regression [local] COMMIT(ExceptionalCondition+0xbe)[0x55f2a101f185] postgres: nkey isolation_regression [local] COMMIT(ProcArrayEndTransaction+0x46)[0x55f2a0ddf7b3] postgres: nkey isolation_regression [local] COMMIT(+0x1e29b1)[0x55f2a09e59b1] postgres: nkey isolation_regression [local] COMMIT(+0x1e347b)[0x55f2a09e647b] postgres: nkey isolation_regression [local] COMMIT(AbortCurrentTransaction+0xe)[0x55f2a09e63a3] postgres: nkey isolation_regression [local] COMMIT(PostgresMain+0x538)[0x55f2a0e20ff1] postgres: nkey isolation_regression [local] COMMIT(+0x61457b)[0x55f2a0e1757b] postgres: nkey isolation_regression [local] COMMIT(postmaster_child_launch+0x137)[0x55f2a0d295bf] postgres: nkey isolation_regression [local] COMMIT(+0x52cff5)[0x55f2a0d2fff5] postgres: nkey isolation_regression [local] COMMIT(+0x52a6cd)[0x55f2a0d2d6cd] postgres: nkey isolation_regression [local] COMMIT(PostmasterMain+0x1629)[0x55f2a0d2cfae] postgres: nkey isolation_regression [local] COMMIT(+0x404ba2)[0x55f2a0c07ba2] /lib/x86_64-linux-gnu/libc.so.6(+0x2a1ca)[0x7f6afbe7e1ca] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x8b)[0x7f6afbe7e28b] postgres: nkey isolation_regression [local] COMMIT(_start+0x25)[0x55f2a08e3ab5] --- src/backend/commands/indexcmds.c | 4 +- src/backend/executor/execIndexing.c | 3 + src/backend/executor/nodeModifyTable.c | 2 + src/backend/utils/time/snapmgr.c | 2 + src/test/modules/injection_points/Makefile | 2 +- .../expected/index_concurrently_upsert.out | 80 +++++++++++++++++++ src/test/modules/injection_points/meson.build | 1 + .../specs/index_concurrently_upsert.spec | 68 ++++++++++++++++ 8 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 src/test/modules/injection_points/expected/index_concurrently_upsert.out create mode 100644 src/test/modules/injection_points/specs/index_concurrently_upsert.spec diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d1134733c17..8f48f14eddd 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1766,6 +1766,7 @@ DefineIndex(Oid tableId, * before the reference snap was taken, we have to wait out any * transactions that might have older snapshots. */ + INJECTION_POINT("define_index_before_set_valid"); pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATEIDX_PHASE_WAIT_3); WaitForOlderSnapshots(limitXmin, true); @@ -4206,7 +4207,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein * the same time to make sure we only get constraint violations from the * indexes with the correct names. */ - + INJECTION_POINT("reindex_relation_concurrently_before_swap"); StartTransactionCommand(); /* @@ -4285,6 +4286,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein * index_drop() for more details. */ + INJECTION_POINT("reindex_relation_concurrently_before_set_dead"); pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE, PROGRESS_CREATEIDX_PHASE_WAIT_4); WaitForLockersMultiple(lockTags, AccessExclusiveLock, true); diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index f9a2fac79e4..5d04f189340 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -117,6 +117,7 @@ #include "utils/multirangetypes.h" #include "utils/rangetypes.h" #include "utils/snapmgr.h" +#include "utils/injection_point.h" /* waitMode argument to check_exclusion_or_unique_constraint() */ typedef enum @@ -936,6 +937,8 @@ retry: econtext->ecxt_scantuple = save_scantuple; ExecDropSingleTupleTableSlot(existing_slot); + if (!conflict) + INJECTION_POINT("check_exclusion_or_unique_constraint_no_conflict"); return !conflict; } diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 1161520f76b..23cf4c6b540 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -69,6 +69,7 @@ #include "utils/datum.h" #include "utils/rel.h" #include "utils/snapmgr.h" +#include "utils/injection_point.h" typedef struct MTTargetRelLookup @@ -1087,6 +1088,7 @@ ExecInsert(ModifyTableContext *context, return NULL; } } + INJECTION_POINT("exec_insert_before_insert_speculative"); /* * Before we start insertion proper, acquire our "speculative diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c index 7d2b34d4f20..3a7357a050d 100644 --- a/src/backend/utils/time/snapmgr.c +++ b/src/backend/utils/time/snapmgr.c @@ -64,6 +64,7 @@ #include "utils/resowner.h" #include "utils/snapmgr.h" #include "utils/syscache.h" +#include "utils/injection_point.h" /* @@ -426,6 +427,7 @@ InvalidateCatalogSnapshot(void) pairingheap_remove(&RegisteredSnapshots, &CatalogSnapshot->ph_node); CatalogSnapshot = NULL; SnapshotResetXmin(); + INJECTION_POINT("invalidate_catalog_snapshot_end"); } } diff --git a/src/test/modules/injection_points/Makefile b/src/test/modules/injection_points/Makefile index 0753a9df58c..3bde3e67503 100644 --- a/src/test/modules/injection_points/Makefile +++ b/src/test/modules/injection_points/Makefile @@ -13,7 +13,7 @@ PGFILEDESC = "injection_points - facility for injection points" REGRESS = injection_points reindex_conc REGRESS_OPTS = --dlpath=$(top_builddir)/src/test/regress -ISOLATION = basic inplace +ISOLATION = basic inplace index_concurrently_upsert TAP_TESTS = 1 diff --git a/src/test/modules/injection_points/expected/index_concurrently_upsert.out b/src/test/modules/injection_points/expected/index_concurrently_upsert.out new file mode 100644 index 00000000000..42dd059d985 --- /dev/null +++ b/src/test/modules/injection_points/expected/index_concurrently_upsert.out @@ -0,0 +1,80 @@ +Parsed test spec with 4 sessions + +starting permutation: s3_start_create_index s1_start_upsert s4_wakeup_define_index_before_set_valid s2_start_upsert s4_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 s3_start_create_index: CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_duplicate ON test.tbl(i); +step s1_start_upsert: INSERT INTO test.tbl VALUES(13,now()) on conflict(i) do update set updated_at = now(); +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(); +step s4_wakeup_s1_from_invalidate_catalog_snapshot: + 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 s2_start_upsert: <... completed> +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 s3_start_create_index: <... completed> +step s1_start_upsert: <... completed> diff --git a/src/test/modules/injection_points/meson.build b/src/test/modules/injection_points/meson.build index 58f19001157..b50140bcf4a 100644 --- a/src/test/modules/injection_points/meson.build +++ b/src/test/modules/injection_points/meson.build @@ -44,6 +44,7 @@ tests += { 'specs': [ 'basic', 'inplace', + 'index_concurrently_upsert', ], }, 'tap': { diff --git a/src/test/modules/injection_points/specs/index_concurrently_upsert.spec b/src/test/modules/injection_points/specs/index_concurrently_upsert.spec new file mode 100644 index 00000000000..075450935b6 --- /dev/null +++ b/src/test/modules/injection_points/specs/index_concurrently_upsert.spec @@ -0,0 +1,68 @@ +# Test race conditions involving: +# - s1: UPSERT a tuple +# - s2: UPSERT the same tuple +# - s3: CREATE UNIQUE INDEX CONCURRENTLY +# - 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'); + 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 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_s1_from_invalidate_catalog_snapshot { + SELECT injection_points_detach('invalidate_catalog_snapshot_end'); + SELECT injection_points_wakeup('invalidate_catalog_snapshot_end'); +} +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'); +} + +permutation + s3_start_create_index + s1_start_upsert + s4_wakeup_define_index_before_set_valid + s2_start_upsert + s4_wakeup_s1_from_invalidate_catalog_snapshot + s4_wakeup_s2 + s4_wakeup_s1 \ No newline at end of file -- 2.43.0