From 7b52ced5c87b94c9a78570f9112c54fda937d1d9 Mon Sep 17 00:00:00 2001 From: Asim R P Date: Mon, 31 Aug 2020 19:41:02 +0530 Subject: [PATCH 4/6] Speculative insert isolation test spec using fault injector This spec simulates a specific interleaving of concurrent speculative inserts that is rather cumbersome to achieve without injecting faults. The interleaving is such that one speculative insert should encounter conflict only after inserting a tuple into heap relation but before inserting it into index relation. Discussion: https://www.postgresql.org/message-id/CAAKRu_a7hbyrk%3DwveHYhr4LbcRnRCG%3DyPUVoQYB9YO1CdUBE9Q%40mail.gmail.com --- src/backend/access/heap/heapam.c | 2 + src/backend/executor/execIndexing.c | 2 + .../expected/insert-conflict-with-faults.out | 25 +++++++++ src/test/isolation/faultinjector_schedule | 1 + .../specs/insert-conflict-with-faults.spec | 53 +++++++++++++++++++ 5 files changed, 83 insertions(+) create mode 100644 src/test/isolation/expected/insert-conflict-with-faults.out create mode 100644 src/test/isolation/faultinjector_schedule create mode 100644 src/test/isolation/specs/insert-conflict-with-faults.spec diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 9b5f417eac4..aaa2dec3aba 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -64,6 +64,7 @@ #include "storage/spin.h" #include "storage/standby.h" #include "utils/datum.h" +#include "utils/faultinjector.h" #include "utils/inval.h" #include "utils/lsyscache.h" #include "utils/relcache.h" @@ -5576,6 +5577,7 @@ heap_abort_speculative(Relation relation, ItemPointer tid) TransactionId prune_xid; Assert(ItemPointerIsValid(tid)); + SIMPLE_FAULT_INJECTOR("heap_abort_speculative"); block = ItemPointerGetBlockNumber(tid); buffer = ReadBuffer(relation, block); diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 1862af621be..294c5eb5ffc 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -114,6 +114,7 @@ #include "executor/executor.h" #include "nodes/nodeFuncs.h" #include "storage/lmgr.h" +#include "utils/faultinjector.h" #include "utils/snapmgr.h" /* waitMode argument to check_exclusion_or_unique_constraint() */ @@ -289,6 +290,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot, bool isnull[INDEX_MAX_KEYS]; Assert(ItemPointerIsValid(tupleid)); + SIMPLE_FAULT_INJECTOR("insert_index_tuples"); /* * Get information from the result relation info structure. diff --git a/src/test/isolation/expected/insert-conflict-with-faults.out b/src/test/isolation/expected/insert-conflict-with-faults.out new file mode 100644 index 00000000000..6ab71bab1f1 --- /dev/null +++ b/src/test/isolation/expected/insert-conflict-with-faults.out @@ -0,0 +1,25 @@ +Parsed test spec with 2 sessions + +starting permutation: s1_upsert s2_upsert s2_unblock_s1 s1_fault_status s1_select +inject_fault + +Success: +step s1_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s1') ON CONFLICT (key) DO UPDATE SET data = upserttest.data || ' with conflict update s1'; +step s2_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s2') ON CONFLICT (key) DO UPDATE SET data = upserttest.data || ' with conflict update s2'; +step s2_unblock_s1: SELECT * FROM inject_fault('insert_index_tuples', 'resume'); +inject_fault + +Success: +step s1_upsert: <... completed> +step s1_fault_status: SELECT * FROM inject_fault('heap_abort_speculative', 'status'); +inject_fault + +Success: fault name:'heap_abort_speculative' fault type:'skip' database name:'' table name:'' start occurrence:'1' end occurrence:'1' extra arg:'0' fault injection state:'completed' num times hit:'1' + +step s1_select: SELECT * FROM upserttest; +key data + +k1 inserted s2 with conflict update s1 +inject_fault + +Success: diff --git a/src/test/isolation/faultinjector_schedule b/src/test/isolation/faultinjector_schedule new file mode 100644 index 00000000000..c658196e614 --- /dev/null +++ b/src/test/isolation/faultinjector_schedule @@ -0,0 +1 @@ +test: insert-conflict-with-faults diff --git a/src/test/isolation/specs/insert-conflict-with-faults.spec b/src/test/isolation/specs/insert-conflict-with-faults.spec new file mode 100644 index 00000000000..32b11d28de5 --- /dev/null +++ b/src/test/isolation/specs/insert-conflict-with-faults.spec @@ -0,0 +1,53 @@ +# INSERT ... ON CONFLICT test verifying that speculative insertion +# failures are handled + +setup +{ + CREATE TABLE upserttest(key text, data text); + CREATE UNIQUE INDEX ON upserttest(key); + + CREATE EXTENSION faultinjector; + -- start with a clean slate + SELECT * FROM inject_fault('all', 'reset'); + + -- inject fault to suspend insert transaction after a tuple has + -- been inserted into the heap but before it is inserted into the + -- index. + SELECT * FROM inject_fault('insert_index_tuples', 'suspend'); + SELECT * FROM inject_fault('heap_abort_speculative', 'skip'); +} + +teardown +{ + DROP TABLE upserttest; + SELECT * FROM inject_fault('all', 'reset'); +} + + +session "s1" +setup +{ + SET default_transaction_isolation = 'read committed'; +} +step "s1_upsert" { INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s1') ON CONFLICT (key) DO UPDATE SET data = upserttest.data || ' with conflict update s1'; } +step "s1_fault_status" { SELECT * FROM inject_fault('heap_abort_speculative', 'status'); } +step "s1_select" { SELECT * FROM upserttest; } + +session "s2" +setup +{ + SET default_transaction_isolation = 'read committed'; +} +step "s2_upsert" { INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s2') ON CONFLICT (key) DO UPDATE SET data = upserttest.data || ' with conflict update s2'; } +step "s2_unblock_s1" { SELECT * FROM inject_fault('insert_index_tuples', 'resume'); } + +# Test that speculative locks are correctly acquired and released, s2 +# inserts, s1 updates. +permutation + # S1 should hit the fault and block + "s1_upsert"& + # S2 should insert without conflict + "s2_upsert" + "s2_unblock_s1" + "s1_fault_status" + "s1_select" -- 2.24.3 (Apple Git-128)