From b536a2fb0c62e9ac1d8e64118e73703376334577 Mon Sep 17 00:00:00 2001
From: Asim R P <apraveen@pivotal.io>
Date: Mon, 19 Aug 2019 13:57:09 +0530
Subject: [PATCH 3/5] 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 +++++++++
 .../specs/insert-conflict-with-faults.spec    | 53 +++++++++++++++++++
 4 files changed, 82 insertions(+)
 create mode 100644 src/test/isolation/expected/insert-conflict-with-faults.out
 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 cb811d345a..4d84245012 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -63,6 +63,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"
@@ -5574,6 +5575,7 @@ heap_abort_speculative(Relation relation, ItemPointer tid)
 	Buffer		buffer;
 
 	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 40bd8049f0..5db2f38dc8 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 0000000000..6ab71bab1f
--- /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'; <waiting ...>
+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/specs/insert-conflict-with-faults.spec b/src/test/isolation/specs/insert-conflict-with-faults.spec
new file mode 100644
index 0000000000..6a7a1d8a02
--- /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';
+}
+blocking 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.17.2 (Apple Git-113)

