Predicate locking in hash indexes.
authorTeodor Sigaev <teodor@sigaev.ru>
Sat, 7 Apr 2018 13:59:14 +0000 (16:59 +0300)
committerTeodor Sigaev <teodor@sigaev.ru>
Sat, 7 Apr 2018 13:59:14 +0000 (16:59 +0300)
Hash index searches acquire predicate locks on the primary
page of a bucket. It acquires a lock on both the old and new buckets
for scans that happen concurrently with page splits. During a bucket
split, a predicate lock is copied from the primary page of an old
bucket to the primary page of a new bucket.

Author: Shubham Barai, Amit Kapila
Reviewed by: Amit Kapila, Alexander Korotkov, Thomas Munro
Discussion: https://www.postgresql.org/message-id/flat/CALxAEPvNsM2GTiXdRgaaZ1Pjd1bs+sxfFsf7Ytr+iq+5JJoYXA@mail.gmail.com

src/backend/access/hash/hash.c
src/backend/access/hash/hashinsert.c
src/backend/access/hash/hashpage.c
src/backend/access/hash/hashsearch.c
src/backend/storage/lmgr/README-SSI
src/test/isolation/expected/predicate-hash.out [new file with mode: 0644]
src/test/isolation/isolation_schedule
src/test/isolation/specs/predicate-hash.spec [new file with mode: 0644]

index e337439adad4f0bc7dbe70d2e0c9db61e1cdc322..4f2ea7955f96eefb738f3fc9ffc899ea5ed2bd68 100644 (file)
@@ -68,7 +68,7 @@ hashhandler(PG_FUNCTION_ARGS)
        amroutine->amsearchnulls = false;
        amroutine->amstorage = false;
        amroutine->amclusterable = false;
-       amroutine->ampredlocks = false;
+       amroutine->ampredlocks = true;
        amroutine->amcanparallel = false;
        amroutine->amkeytype = INT4OID;
 
index f121286b8ca32c3481fe7323e5f91ab4fa7d1e8a..3eb722ce26621df3843e1a0f0331ef0745c6d214 100644 (file)
@@ -22,6 +22,7 @@
 #include "utils/rel.h"
 #include "storage/lwlock.h"
 #include "storage/buf_internals.h"
+#include "storage/predicate.h"
 
 static void _hash_vacuum_one_page(Relation rel, Buffer metabuf, Buffer buf,
                                          RelFileNode hnode);
@@ -88,6 +89,8 @@ restart_insert:
                                                                                  &usedmetap);
        Assert(usedmetap != NULL);
 
+       CheckForSerializableConflictIn(rel, NULL, buf);
+
        /* remember the primary bucket buffer to release the pin on it at end. */
        bucket_buf = buf;
 
index 3859e3bd838fde67eb1d55f37dae06d4c4388be2..3ec29a535689f11cdf64ed665071446d9260a1b2 100644 (file)
@@ -33,6 +33,7 @@
 #include "miscadmin.h"
 #include "storage/lmgr.h"
 #include "storage/smgr.h"
+#include "storage/predicate.h"
 
 
 static bool _hash_alloc_buckets(Relation rel, BlockNumber firstblock,
@@ -1107,6 +1108,11 @@ _hash_splitbucket(Relation rel,
        npage = BufferGetPage(nbuf);
        nopaque = (HashPageOpaque) PageGetSpecialPointer(npage);
 
+       /* Copy the predicate locks from old bucket to new bucket. */
+       PredicateLockPageSplit(rel,
+                                                  BufferGetBlockNumber(bucket_obuf),
+                                                  BufferGetBlockNumber(bucket_nbuf));
+
        /*
         * Partition the tuples in the old bucket between the old bucket and the
         * new bucket, advancing along the old bucket's overflow bucket chain and
index c692c5b32d1b1f9211dad84479d6f3a2f90c7eaf..650041db0a5ca3a0dabb9429326916d07b0ef2c3 100644 (file)
@@ -19,6 +19,7 @@
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "utils/rel.h"
+#include "storage/predicate.h"
 
 static bool _hash_readpage(IndexScanDesc scan, Buffer *bufP,
                           ScanDirection dir);
@@ -171,6 +172,7 @@ _hash_readnext(IndexScanDesc scan,
                Assert(BufferIsValid(*bufp));
 
                LockBuffer(*bufp, BUFFER_LOCK_SHARE);
+               PredicateLockPage(rel, BufferGetBlockNumber(*bufp), scan->xs_snapshot);
 
                /*
                 * setting hashso_buc_split to true indicates that we are scanning
@@ -347,6 +349,7 @@ _hash_first(IndexScanDesc scan, ScanDirection dir)
        so->hashso_sk_hash = hashkey;
 
        buf = _hash_getbucketbuf_from_hashkey(rel, hashkey, HASH_READ, NULL);
+       PredicateLockPage(rel, BufferGetBlockNumber(buf), scan->xs_snapshot);
        page = BufferGetPage(buf);
        TestForOldSnapshot(scan->xs_snapshot, rel, page);
        opaque = (HashPageOpaque) PageGetSpecialPointer(page);
index 9e98af23c837c99324719c1217a40b8463367588..f2b099d1c9ed2ab913d363d01e23b09f33368091 100644 (file)
@@ -389,6 +389,13 @@ relation is required. Fast update postpones the insertion of tuples into index
 structure by temporarily storing them into pending list. That makes us unable
 to detect r-w conflicts using page-level locks.
 
+    * Hash index searches acquire predicate locks on the primary
+page of a bucket. It acquires a lock on both the old and new buckets
+for scans that happen concurrently with page splits. During a bucket
+split, a predicate lock is copied from the primary page of an old
+bucket to the primary page of a new bucket.
+
+
     * The effects of page splits, overflows, consolidations, and
 removals must be carefully reviewed to ensure that predicate locks
 aren't "lost" during those operations, or kept with pages which could
diff --git a/src/test/isolation/expected/predicate-hash.out b/src/test/isolation/expected/predicate-hash.out
new file mode 100644 (file)
index 0000000..53e500f
--- /dev/null
@@ -0,0 +1,659 @@
+Parsed test spec with 2 sessions
+
+starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step c1: commit;
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+600            
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step c2: commit;
+
+starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step c2: commit;
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+400            
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step c1: commit;
+
+starting permutation: rxy3 wx3 c1 rxy4 wy4 c2
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step c1: commit;
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step c2: commit;
+
+starting permutation: rxy4 wy4 c2 rxy3 wx3 c1
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step c2: commit;
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step c1: commit;
+
+starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step c1: commit;
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy1 wx1 rxy2 wy2 c1 c2
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 wx1 rxy2 wy2 c2 c1
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 c1 wy2 c2
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step c1: commit;
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy1 rxy2 wx1 wy2 c1 c2
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wx1 wy2 c2 c1
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c1 c2
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 wx1 c2 c1
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy1 rxy2 wy2 c2 wx1 c1
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step c2: commit;
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy2 rxy1 wx1 c1 wy2 c2
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step c1: commit;
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c2: commit;
+
+starting permutation: rxy2 rxy1 wx1 wy2 c1 c2
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wx1 wy2 c2 c1
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c1 c2
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 wx1 c2 c1
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 rxy1 wy2 c2 wx1 c1
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step c2: commit;
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy2 wy2 rxy1 wx1 c1 c2
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step c1: commit;
+step c2: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 wx1 c2 c1
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+step c2: commit;
+step c1: commit;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxy2 wy2 rxy1 c2 wx1 c1
+step rxy2: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy2: insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g;
+step rxy1: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step c2: commit;
+step wx1: insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g;
+ERROR:  could not serialize access due to read/write dependencies among transactions
+step c1: commit;
+
+starting permutation: rxy3 wx3 rxy4 c1 wy4 c2
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step c1: commit;
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step c2: commit;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c1 c2
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 wx3 rxy4 wy4 c2 c1
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wx3 c1 wy4 c2
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step c1: commit;
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c1 c2
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wx3 wy4 c2 c1
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c1 c2
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy3 rxy4 wy4 wx3 c2 c1
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy3 rxy4 wy4 c2 wx3 c1
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step c2: commit;
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wx3 c1 wy4 c2
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step c1: commit;
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c1 c2
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wx3 wy4 c2 c1
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c1 c2
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 rxy3 wy4 wx3 c2 c1
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 rxy3 wy4 c2 wx3 c1
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step c2: commit;
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step c1: commit;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c1 c2
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step c1: commit;
+step c2: commit;
+
+starting permutation: rxy4 wy4 rxy3 wx3 c2 c1
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step c2: commit;
+step c1: commit;
+
+starting permutation: rxy4 wy4 rxy3 c2 wx3 c1
+step rxy4: select sum(p) from hash_tbl where p=30;
+sum            
+
+300            
+step wy4: insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g;
+step rxy3: select sum(p) from hash_tbl where p=20;
+sum            
+
+200            
+step c2: commit;
+step wx3: insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g;
+step c1: commit;
index 31900cb920bd22b4d0071825775a201800540cc0..87edd8d09bcaf5d92f5fea752a78c7c73dcab411 100644 (file)
@@ -70,6 +70,7 @@ test: async-notify
 test: vacuum-reltuples
 test: timeouts
 test: vacuum-concurrent-drop
+test: predicate-hash
 test: predicate-gist
 test: predicate-gin
 # The checksum_enable suite will enable checksums for the cluster so should
diff --git a/src/test/isolation/specs/predicate-hash.spec b/src/test/isolation/specs/predicate-hash.spec
new file mode 100644 (file)
index 0000000..8c83cb3
--- /dev/null
@@ -0,0 +1,122 @@
+# Test for page level predicate locking in hash index
+#
+# Test to verify serialization failures and to check reduced false positives
+#
+# To verify serialization failures, queries and permutations are written in such
+# a way that an index scan  (from one transaction) and an index insert (from
+# another transaction) will try to access the same bucket of the index
+# whereas to check reduced false positives, they will try to access different
+# buckets of the index.
+
+setup
+{
+ create table hash_tbl(id int4, p integer);
+ create index hash_idx on hash_tbl using hash(p);
+ insert into hash_tbl (id, p)
+ select g, 10 from generate_series(1, 10) g;
+ insert into hash_tbl (id, p)
+ select g, 20 from generate_series(11, 20) g;
+ insert into hash_tbl (id, p)
+ select g, 30 from generate_series(21, 30) g;
+ insert into hash_tbl (id, p)
+ select g, 40 from generate_series(31, 40) g;
+}
+
+teardown
+{
+ drop table hash_tbl;
+}
+
+session "s1"
+setup          
+{
+ begin isolation level serializable;
+ set enable_seqscan=off;
+ set enable_bitmapscan=off;
+ set enable_indexonlyscan=on;
+}
+step "rxy1"    { select sum(p) from hash_tbl where p=20; }
+step "wx1"     { insert into hash_tbl (id, p)
+                         select g, 30 from generate_series(41, 50) g; }
+step "rxy3"    { select sum(p) from hash_tbl where p=20; }
+step "wx3"     { insert into hash_tbl (id, p)
+                         select g, 50 from generate_series(41, 50) g; }
+step "c1"      { commit; }
+
+
+session "s2"
+setup          
+{
+ begin isolation level serializable;
+ set enable_seqscan=off;
+ set enable_bitmapscan=off;
+ set enable_indexonlyscan=on;
+}
+step "rxy2"    { select sum(p) from hash_tbl where p=30; }
+step "wy2"     { insert into hash_tbl (id, p)
+                         select g, 20 from generate_series(51, 60) g; }
+step "rxy4"    { select sum(p) from hash_tbl where p=30; }
+step "wy4"     { insert into hash_tbl (id, p)
+                         select g, 60 from generate_series(51, 60) g; }
+step "c2"      { commit; }
+
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access the same bucket of the index but one transaction
+# commits before other transaction begins so no r-w conflict.
+
+permutation "rxy1" "wx1" "c1" "rxy2" "wy2" "c2"
+permutation "rxy2" "wy2" "c2" "rxy1" "wx1" "c1"
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access different buckets of the index and also one
+# transaction commits before other transaction begins, so no r-w conflict.
+
+permutation "rxy3" "wx3" "c1" "rxy4" "wy4" "c2"
+permutation "rxy4" "wy4" "c2" "rxy3" "wx3" "c1"
+
+
+# An index scan (from one transaction) and an index insert (from another
+# transaction) try to access the same bucket of the index and one transaction
+# begins before other transaction commits so there is a r-w conflict.
+
+permutation "rxy1" "wx1" "rxy2" "c1" "wy2" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c1" "c2"
+permutation "rxy1" "wx1" "rxy2" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wx1" "c1" "wy2" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c1" "c2"
+permutation "rxy1" "rxy2" "wx1" "wy2" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c1" "c2"
+permutation "rxy1" "rxy2" "wy2" "wx1" "c2" "c1"
+permutation "rxy1" "rxy2" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "rxy1" "wx1" "c1" "wy2" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c1" "c2"
+permutation "rxy2" "rxy1" "wx1" "wy2" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c1" "c2"
+permutation "rxy2" "rxy1" "wy2" "wx1" "c2" "c1"
+permutation "rxy2" "rxy1" "wy2" "c2" "wx1" "c1"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c1" "c2"
+permutation "rxy2" "wy2" "rxy1" "wx1" "c2" "c1"
+permutation "rxy2" "wy2" "rxy1" "c2" "wx1" "c1"
+
+# An index scan (from one transaction) and an index insert (from another 
+# transaction) try to access different buckets of the index so no r-w conflict.
+
+permutation "rxy3" "wx3" "rxy4" "c1" "wy4" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c1" "c2"
+permutation "rxy3" "wx3" "rxy4" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wx3" "c1" "wy4" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c1" "c2"
+permutation "rxy3" "rxy4" "wx3" "wy4" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c1" "c2"
+permutation "rxy3" "rxy4" "wy4" "wx3" "c2" "c1"
+permutation "rxy3" "rxy4" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "rxy3" "wx3" "c1" "wy4" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c1" "c2"
+permutation "rxy4" "rxy3" "wx3" "wy4" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c1" "c2"
+permutation "rxy4" "rxy3" "wy4" "wx3" "c2" "c1"
+permutation "rxy4" "rxy3" "wy4" "c2" "wx3" "c1"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c1" "c2"
+permutation "rxy4" "wy4" "rxy3" "wx3" "c2" "c1"
+permutation "rxy4" "wy4" "rxy3" "c2" "wx3" "c1"