From 2ed73e5dbb921a31e7d2f558a683414f8b7c74f0 Mon Sep 17 00:00:00 2001 From: shubhambaraiss Date: Tue, 11 Jul 2017 05:26:52 +0530 Subject: [PATCH] Predicate locking in gin index --- src/backend/access/gin/ginbtree.c | 22 ++ src/backend/access/gin/gindatapage.c | 10 +- src/backend/access/gin/ginget.c | 47 +++- src/backend/access/gin/gininsert.c | 18 +- src/backend/access/gin/ginutil.c | 2 +- src/backend/access/gin/ginvacuum.c | 12 +- src/backend/storage/lmgr/README-SSI | 11 + src/include/access/gin_private.h | 2 +- src/test/isolation/expected/predicate-gin-2.out | 321 ++++++++++++++++++++++ src/test/isolation/expected/predicate-gin.out | 339 ++++++++++++++++++++++++ src/test/isolation/isolation_schedule | 2 + src/test/isolation/specs/predicate-gin-2.spec | 41 +++ src/test/isolation/specs/predicate-gin.spec | 41 +++ 13 files changed, 854 insertions(+), 14 deletions(-) create mode 100644 src/test/isolation/expected/predicate-gin-2.out create mode 100644 src/test/isolation/expected/predicate-gin.out create mode 100644 src/test/isolation/specs/predicate-gin-2.spec create mode 100644 src/test/isolation/specs/predicate-gin.spec diff --git a/src/backend/access/gin/ginbtree.c b/src/backend/access/gin/ginbtree.c index b02cb8a..8184dc0 100644 --- a/src/backend/access/gin/ginbtree.c +++ b/src/backend/access/gin/ginbtree.c @@ -17,6 +17,7 @@ #include "access/gin_private.h" #include "access/ginxlog.h" #include "access/xloginsert.h" +#include "storage/predicate.h" #include "miscadmin.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -515,6 +516,19 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack, btree->fillRoot(btree, newrootpg, BufferGetBlockNumber(lbuffer), newlpage, BufferGetBlockNumber(rbuffer), newrpage); + + if (GinPageIsLeaf(BufferGetPage(stack->buffer))) + { + + PredicateLockPageSplit(btree->index, + BufferGetBlockNumber(stack->buffer), + BufferGetBlockNumber(lbuffer)); + + PredicateLockPageSplit(btree->index, + BufferGetBlockNumber(stack->buffer), + BufferGetBlockNumber(rbuffer)); + } + } else { @@ -524,6 +538,14 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack, GinPageGetOpaque(newrpage)->rightlink = savedRightLink; GinPageGetOpaque(newlpage)->flags |= GIN_INCOMPLETE_SPLIT; GinPageGetOpaque(newlpage)->rightlink = BufferGetBlockNumber(rbuffer); + + if (GinPageIsLeaf(BufferGetPage(stack->buffer))) + { + + PredicateLockPageSplit(btree->index, + BufferGetBlockNumber(stack->buffer), + BufferGetBlockNumber(rbuffer)); + } } /* diff --git a/src/backend/access/gin/gindatapage.c b/src/backend/access/gin/gindatapage.c index 2e5ea47..806c54f 100644 --- a/src/backend/access/gin/gindatapage.c +++ b/src/backend/access/gin/gindatapage.c @@ -20,6 +20,7 @@ #include "lib/ilist.h" #include "miscadmin.h" #include "utils/rel.h" +#include "storage/predicate.h" /* * Min, Max and Target size of posting lists stored on leaf pages, in bytes. @@ -1759,7 +1760,7 @@ leafRepackItems(disassembledLeaf *leaf, ItemPointer remaining) */ BlockNumber createPostingTree(Relation index, ItemPointerData *items, uint32 nitems, - GinStatsData *buildStats) + GinStatsData *buildStats, Buffer entrybuffer) { BlockNumber blkno; Buffer buffer; @@ -1810,6 +1811,12 @@ createPostingTree(Relation index, ItemPointerData *items, uint32 nitems, page = BufferGetPage(buffer); blkno = BufferGetBlockNumber(buffer); + /* + *Copy a predicate lock from entry tree leaf (containing posting list) + *to posting tree. + */ + PredicateLockPageSplit(index, BufferGetBlockNumber(entrybuffer), blkno); + START_CRIT_SECTION(); PageRestoreTempPage(tmppage, page); @@ -1904,6 +1911,7 @@ ginInsertItemPointers(Relation index, BlockNumber rootBlkno, btree.itemptr = insertdata.items[insertdata.curitem]; stack = ginFindLeafPage(&btree, false, NULL); + CheckForSerializableConflictIn(btree.index, NULL, stack->buffer); ginInsertValue(&btree, stack, &insertdata, buildStats); } } diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c index 56a5bf4..3b006d5 100644 --- a/src/backend/access/gin/ginget.c +++ b/src/backend/access/gin/ginget.c @@ -16,9 +16,11 @@ #include "access/gin_private.h" #include "access/relscan.h" +#include "storage/predicate.h" #include "miscadmin.h" #include "utils/datum.h" #include "utils/memutils.h" +#include "utils/rel.h" /* GUC parameter */ int GinFuzzySearchLimit = 0; @@ -37,7 +39,7 @@ typedef struct pendingPosition * Goes to the next page if current offset is outside of bounds */ static bool -moveRightIfItNeeded(GinBtreeData *btree, GinBtreeStack *stack) +moveRightIfItNeeded(GinBtreeData *btree, GinBtreeStack *stack, Snapshot snapshot) { Page page = BufferGetPage(stack->buffer); @@ -51,6 +53,7 @@ moveRightIfItNeeded(GinBtreeData *btree, GinBtreeStack *stack) stack->buffer = ginStepRight(stack->buffer, btree->index, GIN_SHARE); stack->blkno = BufferGetBlockNumber(stack->buffer); + PredicateLockPage(btree->index, stack->blkno, snapshot); stack->off = FirstOffsetNumber; } @@ -73,6 +76,10 @@ scanPostingTree(Relation index, GinScanEntry scanEntry, /* Descend to the leftmost leaf page */ stack = ginScanBeginPostingTree(&btree, index, rootPostingTree, snapshot); buffer = stack->buffer; + + if (!GinGetUseFastUpdate(index)) + PredicateLockPage(index, BufferGetBlockNumber(buffer), snapshot); + IncrBufferRefCount(buffer); /* prevent unpin in freeGinBtreeStack */ freeGinBtreeStack(stack); @@ -94,6 +101,9 @@ scanPostingTree(Relation index, GinScanEntry scanEntry, break; /* no more pages */ buffer = ginStepRight(buffer, index, GIN_SHARE); + + if (!GinGetUseFastUpdate(index)) + PredicateLockPage(index, BufferGetBlockNumber(buffer), snapshot); } UnlockReleaseBuffer(buffer); @@ -131,6 +141,8 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack, attnum = scanEntry->attnum; attr = btree->ginstate->origTupdesc->attrs[attnum - 1]; + PredicateLockPage(btree->index, stack->buffer, snapshot); + for (;;) { Page page; @@ -141,7 +153,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack, /* * stack->off points to the interested entry, buffer is already locked */ - if (moveRightIfItNeeded(btree, stack) == false) + if (moveRightIfItNeeded(btree, stack, snapshot) == false) return true; page = BufferGetPage(stack->buffer); @@ -250,7 +262,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack, Datum newDatum; GinNullCategory newCategory; - if (moveRightIfItNeeded(btree, stack) == false) + if (moveRightIfItNeeded(btree, stack, snapshot) == false) elog(ERROR, "lost saved point in index"); /* must not happen !!! */ page = BufferGetPage(stack->buffer); @@ -323,6 +335,15 @@ restartScanEntry: ginstate); stackEntry = ginFindLeafPage(&btreeEntry, true, snapshot); page = BufferGetPage(stackEntry->buffer); + + /* + * If fast update is enabled, we acquire a predicate lock on the entire + * relation as fast update postpones the insertion of tuples into index + * structure due to which we can't detect rw conflicts. + */ + if (GinGetUseFastUpdate(ginstate->index)) + PredicateLockRelation(ginstate->index, snapshot); + /* ginFindLeafPage() will have already checked snapshot age. */ needUnlock = TRUE; @@ -391,6 +412,9 @@ restartScanEntry: rootPostingTree, snapshot); entry->buffer = stack->buffer; + if (!GinGetUseFastUpdate(ginstate->index)) + PredicateLockPage(ginstate->index, BufferGetBlockNumber(entry->buffer), snapshot); + /* * We keep buffer pinned because we need to prevent deletion of * page during scan. See GIN's vacuum implementation. RefCount is @@ -414,6 +438,8 @@ restartScanEntry: } else if (GinGetNPosting(itup) > 0) { + PredicateLockPage(ginstate->index, BufferGetBlockNumber(stackEntry->buffer), snapshot); + entry->list = ginReadTuple(ginstate, entry->attnum, itup, &entry->nlist); entry->predictNumberResult = entry->nlist; @@ -633,6 +659,9 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry, entry->btree.fullScan = false; stack = ginFindLeafPage(&entry->btree, true, snapshot); + if (!GinGetUseFastUpdate(ginstate->index)) + PredicateLockPage(ginstate->index, BufferGetBlockNumber(stack->buffer), snapshot); + /* we don't need the stack, just the buffer. */ entry->buffer = stack->buffer; IncrBufferRefCount(entry->buffer); @@ -677,6 +706,11 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry, entry->buffer = ginStepRight(entry->buffer, ginstate->index, GIN_SHARE); + + if (!GinGetUseFastUpdate(ginstate->index)) + PredicateLockPage(ginstate->index, BufferGetBlockNumber(entry->buffer), snapshot); + + page = BufferGetPage(entry->buffer); } stepright = true; @@ -1733,6 +1767,13 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids) return; } + /* + * If fast update is disabled, but some items still exist in the pending + * list, then a predicate lock on the entire relation is required. + */ + if (!GinGetUseFastUpdate(scan->indexRelation)) + PredicateLockRelation(scan->indexRelation, scan->xs_snapshot); + pos.pendingBuffer = ReadBuffer(scan->indexRelation, blkno); LockBuffer(pos.pendingBuffer, GIN_SHARE); pos.firstOffset = FirstOffsetNumber; diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c index 5378011..661b605 100644 --- a/src/backend/access/gin/gininsert.c +++ b/src/backend/access/gin/gininsert.c @@ -22,6 +22,7 @@ #include "storage/bufmgr.h" #include "storage/smgr.h" #include "storage/indexfsm.h" +#include "storage/predicate.h" #include "utils/memutils.h" #include "utils/rel.h" @@ -48,7 +49,7 @@ static IndexTuple addItemPointersToLeafTuple(GinState *ginstate, IndexTuple old, ItemPointerData *items, uint32 nitem, - GinStatsData *buildStats) + GinStatsData *buildStats, Buffer buffer) { OffsetNumber attnum; Datum key; @@ -99,7 +100,8 @@ addItemPointersToLeafTuple(GinState *ginstate, postingRoot = createPostingTree(ginstate->index, oldItems, oldNPosting, - buildStats); + buildStats, + buffer); /* Now insert the TIDs-to-be-added into the posting tree */ ginInsertItemPointers(ginstate->index, postingRoot, @@ -127,7 +129,7 @@ static IndexTuple buildFreshLeafTuple(GinState *ginstate, OffsetNumber attnum, Datum key, GinNullCategory category, ItemPointerData *items, uint32 nitem, - GinStatsData *buildStats) + GinStatsData *buildStats, Buffer buffer) { IndexTuple res = NULL; GinPostingList *compressedList; @@ -157,7 +159,7 @@ buildFreshLeafTuple(GinState *ginstate, * Initialize a new posting tree with the TIDs. */ postingRoot = createPostingTree(ginstate->index, items, nitem, - buildStats); + buildStats, buffer); /* And save the root link in the result tuple */ GinSetPostingTree(res, postingRoot); @@ -217,17 +219,19 @@ ginEntryInsert(GinState *ginstate, return; } + CheckForSerializableConflictIn(btree.index, NULL, stack->buffer); /* modify an existing leaf entry */ itup = addItemPointersToLeafTuple(ginstate, itup, - items, nitem, buildStats); + items, nitem, buildStats, stack->buffer); insertdata.isDelete = TRUE; } else { + CheckForSerializableConflictIn(btree.index, NULL, stack->buffer); /* no match, so construct a new leaf entry */ itup = buildFreshLeafTuple(ginstate, attnum, key, category, - items, nitem, buildStats); + items, nitem, buildStats, stack->buffer); } /* Insert the new or modified leaf tuple */ @@ -513,6 +517,8 @@ gininsert(Relation index, Datum *values, bool *isnull, memset(&collector, 0, sizeof(GinTupleCollector)); + CheckForSerializableConflictIn(index, NULL, InvalidBuffer); + for (i = 0; i < ginstate->origTupdesc->natts; i++) ginHeapTupleFastCollect(ginstate, &collector, (OffsetNumber) (i + 1), diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c index 91e4a8c..5cfb45d 100644 --- a/src/backend/access/gin/ginutil.c +++ b/src/backend/access/gin/ginutil.c @@ -49,7 +49,7 @@ ginhandler(PG_FUNCTION_ARGS) amroutine->amsearchnulls = false; amroutine->amstorage = true; amroutine->amclusterable = false; - amroutine->ampredlocks = false; + amroutine->ampredlocks = true; amroutine->amcanparallel = false; amroutine->amkeytype = InvalidOid; diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c index 31425e9..1e52313 100644 --- a/src/backend/access/gin/ginvacuum.c +++ b/src/backend/access/gin/ginvacuum.c @@ -22,6 +22,7 @@ #include "postmaster/autovacuum.h" #include "storage/indexfsm.h" #include "storage/lmgr.h" +#include "storage/predicate.h" #include "utils/memutils.h" struct GinVacuumState @@ -153,11 +154,18 @@ ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkn LockBuffer(lBuffer, GIN_EXCLUSIVE); + page = BufferGetPage(dBuffer); + rightlink = GinPageGetOpaque(page)->rightlink; + + /* + * Any insert which would have gone on the leaf block will now go to its + * right sibling. + */ + PredicateLockPageCombine(gvs->index, deleteBlkno, rightlink); + START_CRIT_SECTION(); /* Unlink the page by changing left sibling's rightlink */ - page = BufferGetPage(dBuffer); - rightlink = GinPageGetOpaque(page)->rightlink; page = BufferGetPage(lBuffer); GinPageGetOpaque(page)->rightlink = rightlink; diff --git a/src/backend/storage/lmgr/README-SSI b/src/backend/storage/lmgr/README-SSI index a9dc01f..6bbdfef 100644 --- a/src/backend/storage/lmgr/README-SSI +++ b/src/backend/storage/lmgr/README-SSI @@ -379,6 +379,17 @@ level during a GiST search. An index insert at the leaf level can then be trusted to ripple up to all levels and locations where conflicting predicate locks may exist. + * Gin searches acquire predicate locks only on the leaf pages +of entry tree and posting tree. We acquire a predicate lock on entry +tree leaf pages only when entry has a posting list. If entry tree has +a pointer to posting tree, we skip locking entry tree leaf page and lock +only posting tree leaf pages. If, however, fast update is enabled, a +predicate lock on the index relation is required as fast update postpones +the insertion of tuples into index structure by temporarily storing them +into pending list due to which we are unable to detect all r-w conflicts. +During a page split, a predicate lock is copied from the original page +to the new page. + * 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/include/access/gin_private.h b/src/include/access/gin_private.h index adfdb0c..03c4103 100644 --- a/src/include/access/gin_private.h +++ b/src/include/access/gin_private.h @@ -217,7 +217,7 @@ extern ItemPointer GinDataLeafPageGetItems(Page page, int *nitems, ItemPointerDa extern int GinDataLeafPageGetItemsToTbm(Page page, TIDBitmap *tbm); extern BlockNumber createPostingTree(Relation index, ItemPointerData *items, uint32 nitems, - GinStatsData *buildStats); + GinStatsData *buildStats, Buffer entrybuffer); extern void GinDataPageAddPostingItem(Page page, PostingItem *data, OffsetNumber offset); extern void GinPageDeletePostingItem(Page page, OffsetNumber offset); extern void ginInsertItemPointers(Relation index, BlockNumber rootBlkno, diff --git a/src/test/isolation/expected/predicate-gin-2.out b/src/test/isolation/expected/predicate-gin-2.out new file mode 100644 index 0000000..b347bce --- /dev/null +++ b/src/test/isolation/expected/predicate-gin-2.out @@ -0,0 +1,321 @@ +Parsed test spec with 2 sessions + +starting permutation: rxy1 wx1 c1 rxy2 wy2 c2 +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step c1: COMMIT; +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step c2: COMMIT; + +starting permutation: rxy1 wx1 rxy2 c1 wy2 c2 +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step c1: COMMIT; +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step c2: COMMIT; + +starting permutation: rxy1 wx1 rxy2 wy2 c1 c2 +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step c1: COMMIT; +step c2: COMMIT; + +starting permutation: rxy1 wx1 rxy2 wy2 c2 c1 +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step c2: COMMIT; +step c1: COMMIT; + +starting permutation: rxy1 rxy2 wx1 c1 wy2 c2 +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step c1: COMMIT; +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step c2: COMMIT; + +starting permutation: rxy1 rxy2 wx1 wy2 c1 c2 +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step c1: COMMIT; +step c2: COMMIT; + +starting permutation: rxy1 rxy2 wx1 wy2 c2 c1 +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step c2: COMMIT; +step c1: COMMIT; + +starting permutation: rxy1 rxy2 wy2 wx1 c1 c2 +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step c1: COMMIT; +step c2: COMMIT; + +starting permutation: rxy1 rxy2 wy2 wx1 c2 c1 +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step c2: COMMIT; +step c1: COMMIT; + +starting permutation: rxy1 rxy2 wy2 c2 wx1 c1 +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step c2: COMMIT; +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step c1: COMMIT; + +starting permutation: rxy2 rxy1 wx1 c1 wy2 c2 +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step c1: COMMIT; +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step c2: COMMIT; + +starting permutation: rxy2 rxy1 wx1 wy2 c1 c2 +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step c1: COMMIT; +step c2: COMMIT; + +starting permutation: rxy2 rxy1 wx1 wy2 c2 c1 +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step c2: COMMIT; +step c1: COMMIT; + +starting permutation: rxy2 rxy1 wy2 wx1 c1 c2 +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step c1: COMMIT; +step c2: COMMIT; + +starting permutation: rxy2 rxy1 wy2 wx1 c2 c1 +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step c2: COMMIT; +step c1: COMMIT; + +starting permutation: rxy2 rxy1 wy2 c2 wx1 c1 +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step c2: COMMIT; +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step c1: COMMIT; + +starting permutation: rxy2 wy2 rxy1 wx1 c1 c2 +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step c1: COMMIT; +step c2: COMMIT; + +starting permutation: rxy2 wy2 rxy1 wx1 c2 c1 +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step c2: COMMIT; +step c1: COMMIT; + +starting permutation: rxy2 wy2 rxy1 c2 wx1 c1 +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step c2: COMMIT; +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step c1: COMMIT; + +starting permutation: rxy2 wy2 c2 rxy1 wx1 c1 +step rxy2: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; +sum + +1007820 +step wy2: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; +step c2: COMMIT; +step rxy1: select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; +sum + +1003860 +step wx1: insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g; +step c1: COMMIT; diff --git a/src/test/isolation/expected/predicate-gin.out b/src/test/isolation/expected/predicate-gin.out new file mode 100644 index 0000000..ab293f6 --- /dev/null +++ b/src/test/isolation/expected/predicate-gin.out @@ -0,0 +1,339 @@ +Parsed test spec with 2 sessions + +starting permutation: rxy1 wx1 c1 rxy2 wy2 c2 +step rxy1: select sum(p[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) g; +step c1: COMMIT; +step rxy2: select sum(p[2]) from gin_tbl where p @> array[3,4]; +sum + +1004 +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) g; +step c2: COMMIT; + +starting permutation: rxy1 wx1 rxy2 c1 wy2 c2 +step rxy1: select sum(p[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) g; +step rxy2: select sum(p[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step c1: COMMIT; +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) g; +step rxy2: select sum(p[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) g; +step rxy2: select sum(p[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step rxy2: select sum(p[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) g; +step c1: COMMIT; +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step rxy2: select sum(p[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) g; +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step rxy2: select sum(p[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) g; +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step rxy2: select sum(p[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) g; +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step rxy2: select sum(p[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) g; +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step rxy2: select sum(p[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) g; +step c2: COMMIT; +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step rxy1: select sum(p[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) g; +step c1: COMMIT; +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step rxy1: select sum(p[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) g; +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step rxy1: select sum(p[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) g; +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step rxy1: select sum(p[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) g; +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step rxy1: select sum(p[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) g; +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step rxy1: select sum(p[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) g; +step c2: COMMIT; +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) g; +step rxy1: select sum(p[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) g; +step rxy1: select sum(p[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) 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[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) g; +step rxy1: select sum(p[2]) from gin_tbl where p @> array[1,2]; +sum + +400 +step c2: COMMIT; +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) g; +ERROR: could not serialize access due to read/write dependencies among transactions +step c1: COMMIT; + +starting permutation: rxy2 wy2 c2 rxy1 wx1 c1 +step rxy2: select sum(p[2]) from gin_tbl where p @> array[3,4]; +sum + +800 +step wy2: insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) g; +step c2: COMMIT; +step rxy1: select sum(p[2]) from gin_tbl where p @> array[1,2]; +sum + +502 +step wx1: insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) g; +step c1: COMMIT; diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule index 32c965b..3099d8b 100644 --- a/src/test/isolation/isolation_schedule +++ b/src/test/isolation/isolation_schedule @@ -62,3 +62,5 @@ test: sequence-ddl test: async-notify test: vacuum-reltuples test: timeouts +test: predicate-gin +test: predicate-gin-2 diff --git a/src/test/isolation/specs/predicate-gin-2.spec b/src/test/isolation/specs/predicate-gin-2.spec new file mode 100644 index 0000000..baac108 --- /dev/null +++ b/src/test/isolation/specs/predicate-gin-2.spec @@ -0,0 +1,41 @@ +# Test for page level predicate locking in gin +# +# Test to check reduced false positives. +# +# Queries are written in such a way that an index scan(from one transaction) and an index insert(from another transaction) will try to access different parts(sub-tree) of the index. + + +setup +{ + create table gin_tbl(id int4, p int4[]); + create index ginidx on gin_tbl using gin(p) with + (fastupdate = off); + insert into gin_tbl select g, array[1, g*2, 2] from generate_series(11, 1000) g; + insert into gin_tbl select g, array[3, g*2, 4] from generate_series(11, 1000) g; +} + +teardown +{ + DROP TABLE gin_tbl; +} + +session "s1" +setup { + BEGIN ISOLATION LEVEL SERIALIZABLE; + set enable_seqscan=off; + } +step "rxy1" { select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[1,2]; } +step "wx1" { insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(3000, 3100) g;} +step "c1" { COMMIT; } + +session "s2" +setup { + BEGIN ISOLATION LEVEL SERIALIZABLE; + set enable_seqscan=off; + } + +step "rxy2" { select sum(p[1]+p[2]+p[3]) from gin_tbl where p @> array[3,4]; } +step "wy2" { insert into gin_tbl select g, array[g, g*2, g*3] + from generate_series(4000, 4100) g; } +step "c2" { COMMIT; } diff --git a/src/test/isolation/specs/predicate-gin.spec b/src/test/isolation/specs/predicate-gin.spec new file mode 100644 index 0000000..7d9f446 --- /dev/null +++ b/src/test/isolation/specs/predicate-gin.spec @@ -0,0 +1,41 @@ +# Test for page level predicate locking in gin +# +# Test to verify serialization failures +# +# Queries 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. + + +setup +{ + create table gin_tbl(id int4, p int4[]); + create index ginidx on gin_tbl using gin(p) with + (fastupdate = off); + insert into gin_tbl select g, array[1, 2, g*2] from generate_series(1, 200) g; + insert into gin_tbl select g, array[3, 4, g*3] from generate_series(1, 200) g; +} + +teardown +{ + DROP TABLE gin_tbl; +} + +session "s1" +setup { + BEGIN ISOLATION LEVEL SERIALIZABLE; + set enable_seqscan=off; + } +step "rxy1" { select sum(p[2]) from gin_tbl where p @> array[1,2]; } +step "wx1" { insert into gin_tbl select g, array[3, 4, g*3] + from generate_series(200, 250) g;} +step "c1" { COMMIT; } + +session "s2" +setup { + BEGIN ISOLATION LEVEL SERIALIZABLE; + set enable_seqscan=off; + } + +step "rxy2" { select sum(p[2]) from gin_tbl where p @> array[3,4]; } +step "wy2" { insert into gin_tbl select g, array[1, 2, g*2] + from generate_series(200, 250) g; } +step "c2" { COMMIT; } -- 1.9.1