On Wed, May 15, 2019 at 8:36 PM Melanie Plageman <melanieplage...@gmail.com>
wrote:

>
> On Wed, May 15, 2019 at 6:50 PM Andres Freund <and...@anarazel.de> wrote:
>
>>
>> > I noticed that there is not a test case which would cover the
>> speculative
>> > wait
>> > codepath. This seems much more challenging, however, it does seem like a
>> > worthwhile test to have.
>>
>> Shouldn't be that hard to create, I think. I think acquiring another
>> lock in a second, non-unique, expression index, ought to do the trick?
>> It probably has to be created after the unique index (so it's later in
>> the
>>
>> I would think that the sequence would be s1 and s2 probe the index, s1
> and s2
> insert into the table, s1 updates the index but does not complete the
> speculative insert and clear the token (pause before
> table_complete_speculative). s2 is in speculative wait when attempting to
> update
> the index.
>
> Something like
>
> permutation
>    "controller_locks"
>    "controller_show"
>    "s1_upsert" "s2_upsert"
>    "controller_show"
>    "controller_unlock_1_1" "controller_unlock_2_1"
>    "controller_unlock_1_3" "controller_unlock_2_3"
>    "controller_unlock_1_2"
>    "s1_magically_pause_before_complete_speculative"
>    # put s2 in speculative wait
>    "controller_unlock_2_2"
>    "s1_magically_unpause_before_complete_speculative"
>
> So, how would another lock on another index keep s1 from clearing the
> speculative token after it has updated the index?
>

The second index would help to hold the session after inserting the tuple
in unique index but before completing the speculative insert. Hence, helps
to create the condition easily. I believe order of index insertion is
helping here that unique index is inserted and then non-unique index is
inserted too.

Attaching patch with the test using the idea Andres mentioned and it works
to excercise the speculative wait.
From 24e44f1a971e8957503205376d655e22b0e5fbdc Mon Sep 17 00:00:00 2001
From: Ashwin Agrawal <aagra...@pivotal.io>
Date: Wed, 15 May 2019 22:21:48 -0700
Subject: [PATCH] Add test to validate speculative wait is performed.

---
 .../expected/insert-conflict-specconflict.out | 69 +++++++++++++++++++
 .../specs/insert-conflict-specconflict.spec   | 44 ++++++++++++
 2 files changed, 113 insertions(+)

diff --git a/src/test/isolation/expected/insert-conflict-specconflict.out b/src/test/isolation/expected/insert-conflict-specconflict.out
index 5726bdb8e8..ca2b65ca56 100644
--- a/src/test/isolation/expected/insert-conflict-specconflict.out
+++ b/src/test/isolation/expected/insert-conflict-specconflict.out
@@ -177,3 +177,72 @@ step controller_show: SELECT * FROM upserttest;
 key            data           
 
 k1             inserted s1 with conflict update s2
+
+starting permutation: s1_create_non_unique_index controller_locks controller_show s1_upsert s2_upsert controller_show controller_unlock_1_1 controller_unlock_2_1 controller_unlock_1_3 controller_unlock_2_3 controller_show controller_lock_2_4 controller_unlock_2_2 controller_show controller_unlock_1_2 controller_print_speculative_locks controller_unlock_2_4 controller_show
+step s1_create_non_unique_index: CREATE INDEX ON upserttest((blurt_and_lock2(key)));
+step controller_locks: SELECT pg_advisory_lock(sess, lock), sess, lock FROM generate_series(1, 2) a(sess), generate_series(1,3) b(lock);
+pg_advisory_locksess           lock           
+
+               1              1              
+               1              2              
+               1              3              
+               2              1              
+               2              2              
+               2              3              
+step controller_show: SELECT * FROM upserttest;
+key            data           
+
+step s1_upsert: INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s1') ON CONFLICT (blurt_and_lock(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 (blurt_and_lock(key)) DO UPDATE SET data = upserttest.data || ' with conflict update s2'; <waiting ...>
+step controller_show: SELECT * FROM upserttest;
+key            data           
+
+step controller_unlock_1_1: SELECT pg_advisory_unlock(1, 1);
+pg_advisory_unlock
+
+t              
+step controller_unlock_2_1: SELECT pg_advisory_unlock(2, 1);
+pg_advisory_unlock
+
+t              
+step controller_unlock_1_3: SELECT pg_advisory_unlock(1, 3);
+pg_advisory_unlock
+
+t              
+step controller_unlock_2_3: SELECT pg_advisory_unlock(2, 3);
+pg_advisory_unlock
+
+t              
+step controller_show: SELECT * FROM upserttest;
+key            data           
+
+step controller_lock_2_4: SELECT pg_advisory_lock(2, 4);
+pg_advisory_lock
+
+               
+step controller_unlock_2_2: SELECT pg_advisory_unlock(2, 2);
+pg_advisory_unlock
+
+t              
+step controller_show: SELECT * FROM upserttest;
+key            data           
+
+step controller_unlock_1_2: SELECT pg_advisory_unlock(1, 2);
+pg_advisory_unlock
+
+t              
+step controller_print_speculative_locks: SELECT locktype,classid,objid,mode,granted FROM pg_locks WHERE locktype='speculative token' order by granted;
+locktype       classid        objid          mode           granted        
+
+speculative token4              0              ShareLock      f              
+speculative token4              0              ExclusiveLock  t              
+step controller_unlock_2_4: SELECT pg_advisory_unlock(2, 4);
+pg_advisory_unlock
+
+t              
+step s1_upsert: <... completed>
+step s2_upsert: <... completed>
+step controller_show: SELECT * FROM upserttest;
+key            data           
+
+k1             inserted s2 with conflict update s1
diff --git a/src/test/isolation/specs/insert-conflict-specconflict.spec b/src/test/isolation/specs/insert-conflict-specconflict.spec
index 3a70484fc2..71e46727cf 100644
--- a/src/test/isolation/specs/insert-conflict-specconflict.spec
+++ b/src/test/isolation/specs/insert-conflict-specconflict.spec
@@ -23,6 +23,13 @@ setup
     RETURN $1;
     END;$$;
 
+    CREATE OR REPLACE FUNCTION blurt_and_lock2(text) RETURNS text IMMUTABLE LANGUAGE plpgsql AS $$
+    BEGIN
+        RAISE NOTICE 'blurt_and_lock1 called for %', $1;
+        PERFORM pg_advisory_xact_lock(current_setting('spec.session')::int, 4);
+    RETURN $1;
+    END;$$;
+
     CREATE TABLE upserttest(key text, data text);
 
     CREATE UNIQUE INDEX ON upserttest((blurt_and_lock(key)));
@@ -45,7 +52,10 @@ step "controller_unlock_1_2" { SELECT pg_advisory_unlock(1, 2); }
 step "controller_unlock_2_2" { SELECT pg_advisory_unlock(2, 2); }
 step "controller_unlock_1_3" { SELECT pg_advisory_unlock(1, 3); }
 step "controller_unlock_2_3" { SELECT pg_advisory_unlock(2, 3); }
+step "controller_lock_2_4" { SELECT pg_advisory_lock(2, 4); }
+step "controller_unlock_2_4" { SELECT pg_advisory_unlock(2, 4); }
 step "controller_show" {SELECT * FROM upserttest; }
+step "controller_print_speculative_locks" { SELECT locktype,classid,objid,mode,granted FROM pg_locks WHERE locktype='speculative token' order by granted; }
 
 session "s1"
 setup
@@ -54,6 +64,7 @@ setup
   SET spec.session = 1;
 }
 step "s1_begin"  { BEGIN; }
+step "s1_create_non_unique_index" { CREATE INDEX ON upserttest((blurt_and_lock2(key))); }
 step "s1_upsert" { INSERT INTO upserttest(key, data) VALUES('k1', 'inserted s1') ON CONFLICT (blurt_and_lock(key)) DO UPDATE SET data = upserttest.data || ' with conflict update s1'; }
 step "s1_commit"  { COMMIT; }
 
@@ -147,3 +158,36 @@ permutation
    "controller_show"
    "s2_commit"
    "controller_show"
+
+# Test that speculative wait is performed if session sees a speculatively
+# inserted tuple. Speculatively inserted tuple means tuple is inserted into the
+# table and unique index but yet to *complete* the speculative insertion
+permutation
+   # acquire a number of locks, to control execution flow - the
+   # blurt_and_lock function acquires advisory locks that allow us to
+   # continue after a) the optimistic conflict probe b) after the
+   # insertion of the speculative tuple.
+   "s1_create_non_unique_index"
+   "controller_locks"
+   "controller_show"
+   "s1_upsert" "s2_upsert"
+   "controller_show"
+   # Switch both sessions to wait on the other lock next time (the speculative insertion)
+   "controller_unlock_1_1" "controller_unlock_2_1"
+   # Allow both sessions to continue
+   "controller_unlock_1_3" "controller_unlock_2_3"
+   "controller_show"
+   # take lock to block second session after inserting in unique index but
+   # before completing the speculative insert
+   "controller_lock_2_4"
+   # Allow the second session to move forward
+   "controller_unlock_2_2"
+   # This should still not show a successful insertion
+   "controller_show"
+   # Allow the first session, it should perform speculative wait
+   "controller_unlock_1_2"
+   # Should report waiting on speculative lock
+   "controller_print_speculative_locks"
+   "controller_unlock_2_4"
+   # This should now show a successful UPSERT
+   "controller_show"
-- 
2.19.1

Reply via email to