2018-03-30 13:23:17 +02:00
|
|
|
# Test for page level predicate locking in gin 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 part (sub-tree) of the index
|
|
|
|
# whereas to check reduced false positives, they will try to access different
|
|
|
|
# parts (sub-tree) of the index.
|
|
|
|
|
|
|
|
|
|
|
|
setup
|
|
|
|
{
|
|
|
|
create table gin_tbl(id int4, p int4[]);
|
|
|
|
insert into gin_tbl select g, array[g, g*2,g*3] from generate_series(1, 10000) g;
|
|
|
|
insert into gin_tbl select g, array[4,5,6] from generate_series(10001, 20000) g;
|
|
|
|
create index ginidx on gin_tbl using gin(p) with (fastupdate = off);
|
|
|
|
}
|
|
|
|
|
|
|
|
teardown
|
|
|
|
{
|
|
|
|
drop table gin_tbl;
|
|
|
|
}
|
|
|
|
|
|
|
|
session "s1"
|
|
|
|
setup
|
|
|
|
{
|
|
|
|
begin isolation level serializable;
|
|
|
|
set enable_seqscan=off;
|
|
|
|
}
|
|
|
|
|
|
|
|
# enable pending list for a small subset of tests
|
|
|
|
step "fu1" { alter index ginidx set (fastupdate = on);
|
|
|
|
commit;
|
Re-think predicate locking on GIN indexes.
The principle behind the locking was not very well thought-out, and not
documented. Add a section in the README to explain how it's supposed to
work, and change the code so that it actually works that way.
This fixes two bugs:
1. If fast update was turned on concurrently, subsequent inserts to the
pending list would not conflict with predicate locks that were acquired
earlier, on entry pages. The included 'predicate-gin-fastupdate' test
demonstrates that. To fix, make all scans acquire a predicate lock on
the metapage. That lock represents a scan of the pending list, whether
or not there is a pending list at the moment. Forget about the
optimization to skip locking/checking for locks, when fastupdate=off.
2. If a scan finds no match, it still needs to lock the entry page. The
point of predicate locks is to lock the gabs between values, whether
or not there is a match. The included 'predicate-gin-nomatch' test
tests that case.
In addition to those two bug fixes, this removes some unnecessary locking,
following the principle laid out in the README. Because all items in
a posting tree have the same key value, a lock on the posting tree root is
enough to cover all the items. (With a very large posting tree, it would
possibly be better to lock the posting tree leaf pages instead, so that a
"skip scan" with a query like "A & B", you could avoid unnecessary conflict
if a new tuple is inserted with A but !B. But let's keep this simple.)
Also, some spelling fixes.
Author: Heikki Linnakangas with some editorization by me
Review: Andrey Borodin, Alexander Korotkov
Discussion: https://www.postgresql.org/message-id/0b3ad2c2-2692-62a9-3a04-5724f2af9114@iki.fi
2018-05-04 10:27:50 +02:00
|
|
|
begin isolation level serializable;
|
|
|
|
set enable_seqscan=off; }
|
2018-03-30 13:23:17 +02:00
|
|
|
|
|
|
|
step "rxy1" { select count(*) from gin_tbl where p @> array[4,5]; }
|
|
|
|
step "wx1" { insert into gin_tbl select g, array[5,6] from generate_series
|
|
|
|
(20001, 20050) g; }
|
|
|
|
step "rxy3" { select count(*) from gin_tbl where p @> array[1,2] or
|
|
|
|
p @> array[100,200] or p @> array[500,1000] or p @> array[1000,2000]; }
|
|
|
|
step "wx3" { insert into gin_tbl select g, array[g,g*2] from generate_series
|
|
|
|
(1, 50) g; }
|
|
|
|
step "c1" { commit; }
|
|
|
|
|
|
|
|
session "s2"
|
|
|
|
setup
|
|
|
|
{
|
|
|
|
begin isolation level serializable;
|
|
|
|
set enable_seqscan=off;
|
|
|
|
}
|
|
|
|
|
|
|
|
step "rxy2" { select count(*) from gin_tbl where p @> array[5,6]; }
|
|
|
|
step "rxy2fu" { select count(*) from gin_tbl where p @> array[10000,10005]; }
|
|
|
|
step "wy2" { insert into gin_tbl select g, array[4,5] from
|
|
|
|
generate_series(20051, 20100) g; }
|
|
|
|
step "wy2fu" { insert into gin_tbl select g, array[10000,10005] from
|
|
|
|
generate_series(20051, 20100) g; }
|
|
|
|
step "rxy4" { select count(*) from gin_tbl where p @> array[4000,8000] or
|
|
|
|
p @> array[5000,10000] or p @> array[6000,12000] or
|
|
|
|
p @> array[8000,16000]; }
|
|
|
|
step "wy4" { insert into gin_tbl select g, array[g,g*2] from generate_series
|
|
|
|
(10000, 10050) g; }
|
|
|
|
step "c2" { commit; }
|
|
|
|
|
|
|
|
|
|
|
|
# An index scan (from one transaction) and an index insert (from another transaction)
|
|
|
|
# try to access the same part 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 parts 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 part 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 parts 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"
|
|
|
|
|
|
|
|
# Test fastupdate = on. First test should pass because fastupdate is off and
|
|
|
|
# sessions touches different parts of index, second should fail because
|
|
|
|
# with fastupdate on, then whole index should be under predicate lock.
|
|
|
|
|
|
|
|
permutation "rxy1" "rxy2fu" "wx1" "c1" "wy2fu" "c2"
|
|
|
|
permutation "fu1" "rxy1" "rxy2fu" "wx1" "c1" "wy2fu" "c2"
|