Predicate locking in GIN index
Predicate locks are used on per page basis only if fastupdate = off, in opposite case predicate lock on pending list will effectively lock whole index, to reduce locking overhead, just lock a relation. Entry and posting trees are essentially B-tree, so locks are acquired on leaf pages only. Author: Shubham Barai with some editorization by me and Dmitry Ivanov Review by: Alexander Korotkov, Dmitry Ivanov, Fedor Sigaev Discussion: https://www.postgresql.org/message-id/flat/CALxAEPt5sWW+EwTaKUGFL5_XFcZ0MuGBcyJ70oqbWqr42YKR8Q@mail.gmail.com
This commit is contained in:
parent
019fa576ca
commit
43d1ed60fd
@ -17,6 +17,7 @@
|
|||||||
#include "access/gin_private.h"
|
#include "access/gin_private.h"
|
||||||
#include "access/ginxlog.h"
|
#include "access/ginxlog.h"
|
||||||
#include "access/xloginsert.h"
|
#include "access/xloginsert.h"
|
||||||
|
#include "storage/predicate.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
@ -515,6 +516,19 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
|
|||||||
btree->fillRoot(btree, newrootpg,
|
btree->fillRoot(btree, newrootpg,
|
||||||
BufferGetBlockNumber(lbuffer), newlpage,
|
BufferGetBlockNumber(lbuffer), newlpage,
|
||||||
BufferGetBlockNumber(rbuffer), newrpage);
|
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
|
else
|
||||||
{
|
{
|
||||||
@ -524,6 +538,14 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
|
|||||||
GinPageGetOpaque(newrpage)->rightlink = savedRightLink;
|
GinPageGetOpaque(newrpage)->rightlink = savedRightLink;
|
||||||
GinPageGetOpaque(newlpage)->flags |= GIN_INCOMPLETE_SPLIT;
|
GinPageGetOpaque(newlpage)->flags |= GIN_INCOMPLETE_SPLIT;
|
||||||
GinPageGetOpaque(newlpage)->rightlink = BufferGetBlockNumber(rbuffer);
|
GinPageGetOpaque(newlpage)->rightlink = BufferGetBlockNumber(rbuffer);
|
||||||
|
|
||||||
|
if (GinPageIsLeaf(BufferGetPage(stack->buffer)))
|
||||||
|
{
|
||||||
|
|
||||||
|
PredicateLockPageSplit(btree->index,
|
||||||
|
BufferGetBlockNumber(stack->buffer),
|
||||||
|
BufferGetBlockNumber(rbuffer));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "access/xloginsert.h"
|
#include "access/xloginsert.h"
|
||||||
#include "lib/ilist.h"
|
#include "lib/ilist.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
|
#include "storage/predicate.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1759,7 +1760,7 @@ leafRepackItems(disassembledLeaf *leaf, ItemPointer remaining)
|
|||||||
*/
|
*/
|
||||||
BlockNumber
|
BlockNumber
|
||||||
createPostingTree(Relation index, ItemPointerData *items, uint32 nitems,
|
createPostingTree(Relation index, ItemPointerData *items, uint32 nitems,
|
||||||
GinStatsData *buildStats)
|
GinStatsData *buildStats, Buffer entrybuffer)
|
||||||
{
|
{
|
||||||
BlockNumber blkno;
|
BlockNumber blkno;
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
@ -1810,6 +1811,12 @@ createPostingTree(Relation index, ItemPointerData *items, uint32 nitems,
|
|||||||
page = BufferGetPage(buffer);
|
page = BufferGetPage(buffer);
|
||||||
blkno = BufferGetBlockNumber(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();
|
START_CRIT_SECTION();
|
||||||
|
|
||||||
PageRestoreTempPage(tmppage, page);
|
PageRestoreTempPage(tmppage, page);
|
||||||
@ -1904,6 +1911,7 @@ ginInsertItemPointers(Relation index, BlockNumber rootBlkno,
|
|||||||
btree.itemptr = insertdata.items[insertdata.curitem];
|
btree.itemptr = insertdata.items[insertdata.curitem];
|
||||||
stack = ginFindLeafPage(&btree, false, NULL);
|
stack = ginFindLeafPage(&btree, false, NULL);
|
||||||
|
|
||||||
|
GinCheckForSerializableConflictIn(btree.index, NULL, stack->buffer);
|
||||||
ginInsertValue(&btree, stack, &insertdata, buildStats);
|
ginInsertValue(&btree, stack, &insertdata, buildStats);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,10 @@
|
|||||||
#include "access/gin_private.h"
|
#include "access/gin_private.h"
|
||||||
#include "access/relscan.h"
|
#include "access/relscan.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
|
#include "storage/predicate.h"
|
||||||
#include "utils/datum.h"
|
#include "utils/datum.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
|
#include "utils/rel.h"
|
||||||
|
|
||||||
/* GUC parameter */
|
/* GUC parameter */
|
||||||
int GinFuzzySearchLimit = 0;
|
int GinFuzzySearchLimit = 0;
|
||||||
@ -33,11 +35,25 @@ typedef struct pendingPosition
|
|||||||
} pendingPosition;
|
} pendingPosition;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Place predicate lock on GIN page if needed.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
GinPredicateLockPage(Relation index, BlockNumber blkno, Snapshot snapshot)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* When fast update is on then no need in locking pages, because we
|
||||||
|
* anyway need to lock the whole index.
|
||||||
|
*/
|
||||||
|
if (!GinGetUseFastUpdate(index))
|
||||||
|
PredicateLockPage(index, blkno, snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Goes to the next page if current offset is outside of bounds
|
* Goes to the next page if current offset is outside of bounds
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
moveRightIfItNeeded(GinBtreeData *btree, GinBtreeStack *stack)
|
moveRightIfItNeeded(GinBtreeData *btree, GinBtreeStack *stack, Snapshot snapshot)
|
||||||
{
|
{
|
||||||
Page page = BufferGetPage(stack->buffer);
|
Page page = BufferGetPage(stack->buffer);
|
||||||
|
|
||||||
@ -52,6 +68,7 @@ moveRightIfItNeeded(GinBtreeData *btree, GinBtreeStack *stack)
|
|||||||
stack->buffer = ginStepRight(stack->buffer, btree->index, GIN_SHARE);
|
stack->buffer = ginStepRight(stack->buffer, btree->index, GIN_SHARE);
|
||||||
stack->blkno = BufferGetBlockNumber(stack->buffer);
|
stack->blkno = BufferGetBlockNumber(stack->buffer);
|
||||||
stack->off = FirstOffsetNumber;
|
stack->off = FirstOffsetNumber;
|
||||||
|
GinPredicateLockPage(btree->index, stack->blkno, snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -73,6 +90,7 @@ scanPostingTree(Relation index, GinScanEntry scanEntry,
|
|||||||
/* Descend to the leftmost leaf page */
|
/* Descend to the leftmost leaf page */
|
||||||
stack = ginScanBeginPostingTree(&btree, index, rootPostingTree, snapshot);
|
stack = ginScanBeginPostingTree(&btree, index, rootPostingTree, snapshot);
|
||||||
buffer = stack->buffer;
|
buffer = stack->buffer;
|
||||||
|
|
||||||
IncrBufferRefCount(buffer); /* prevent unpin in freeGinBtreeStack */
|
IncrBufferRefCount(buffer); /* prevent unpin in freeGinBtreeStack */
|
||||||
|
|
||||||
freeGinBtreeStack(stack);
|
freeGinBtreeStack(stack);
|
||||||
@ -82,6 +100,11 @@ scanPostingTree(Relation index, GinScanEntry scanEntry,
|
|||||||
*/
|
*/
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Predicate lock each leaf page in posting tree
|
||||||
|
*/
|
||||||
|
GinPredicateLockPage(index, BufferGetBlockNumber(buffer), snapshot);
|
||||||
|
|
||||||
page = BufferGetPage(buffer);
|
page = BufferGetPage(buffer);
|
||||||
if ((GinPageGetOpaque(page)->flags & GIN_DELETED) == 0)
|
if ((GinPageGetOpaque(page)->flags & GIN_DELETED) == 0)
|
||||||
{
|
{
|
||||||
@ -131,6 +154,12 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
|
|||||||
attnum = scanEntry->attnum;
|
attnum = scanEntry->attnum;
|
||||||
attr = TupleDescAttr(btree->ginstate->origTupdesc, attnum - 1);
|
attr = TupleDescAttr(btree->ginstate->origTupdesc, attnum - 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Predicate lock entry leaf page, following pages will be locked by
|
||||||
|
* moveRightIfItNeeded()
|
||||||
|
*/
|
||||||
|
GinPredicateLockPage(btree->index, stack->buffer, snapshot);
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
Page page;
|
Page page;
|
||||||
@ -141,7 +170,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
|
|||||||
/*
|
/*
|
||||||
* stack->off points to the interested entry, buffer is already locked
|
* stack->off points to the interested entry, buffer is already locked
|
||||||
*/
|
*/
|
||||||
if (moveRightIfItNeeded(btree, stack) == false)
|
if (moveRightIfItNeeded(btree, stack, snapshot) == false)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
page = BufferGetPage(stack->buffer);
|
page = BufferGetPage(stack->buffer);
|
||||||
@ -250,7 +279,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
|
|||||||
Datum newDatum;
|
Datum newDatum;
|
||||||
GinNullCategory newCategory;
|
GinNullCategory newCategory;
|
||||||
|
|
||||||
if (moveRightIfItNeeded(btree, stack) == false)
|
if (moveRightIfItNeeded(btree, stack, snapshot) == false)
|
||||||
elog(ERROR, "lost saved point in index"); /* must not happen !!! */
|
elog(ERROR, "lost saved point in index"); /* must not happen !!! */
|
||||||
|
|
||||||
page = BufferGetPage(stack->buffer);
|
page = BufferGetPage(stack->buffer);
|
||||||
@ -323,6 +352,7 @@ restartScanEntry:
|
|||||||
ginstate);
|
ginstate);
|
||||||
stackEntry = ginFindLeafPage(&btreeEntry, true, snapshot);
|
stackEntry = ginFindLeafPage(&btreeEntry, true, snapshot);
|
||||||
page = BufferGetPage(stackEntry->buffer);
|
page = BufferGetPage(stackEntry->buffer);
|
||||||
|
|
||||||
/* ginFindLeafPage() will have already checked snapshot age. */
|
/* ginFindLeafPage() will have already checked snapshot age. */
|
||||||
needUnlock = true;
|
needUnlock = true;
|
||||||
|
|
||||||
@ -370,6 +400,10 @@ restartScanEntry:
|
|||||||
{
|
{
|
||||||
IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stackEntry->off));
|
IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stackEntry->off));
|
||||||
|
|
||||||
|
/* Predicate lock visited entry leaf page */
|
||||||
|
GinPredicateLockPage(ginstate->index,
|
||||||
|
BufferGetBlockNumber(stackEntry->buffer), snapshot);
|
||||||
|
|
||||||
if (GinIsPostingTree(itup))
|
if (GinIsPostingTree(itup))
|
||||||
{
|
{
|
||||||
BlockNumber rootPostingTree = GinGetPostingTree(itup);
|
BlockNumber rootPostingTree = GinGetPostingTree(itup);
|
||||||
@ -391,6 +425,12 @@ restartScanEntry:
|
|||||||
rootPostingTree, snapshot);
|
rootPostingTree, snapshot);
|
||||||
entry->buffer = stack->buffer;
|
entry->buffer = stack->buffer;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Predicate lock visited posting tree page, following pages
|
||||||
|
* will be locked by moveRightIfItNeeded or entryLoadMoreItems
|
||||||
|
*/
|
||||||
|
GinPredicateLockPage(ginstate->index, BufferGetBlockNumber(entry->buffer), snapshot);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We keep buffer pinned because we need to prevent deletion of
|
* We keep buffer pinned because we need to prevent deletion of
|
||||||
* page during scan. See GIN's vacuum implementation. RefCount is
|
* page during scan. See GIN's vacuum implementation. RefCount is
|
||||||
@ -493,7 +533,7 @@ startScanKey(GinState *ginstate, GinScanOpaque so, GinScanKey key)
|
|||||||
|
|
||||||
for (i = 0; i < key->nentries - 1; i++)
|
for (i = 0; i < key->nentries - 1; i++)
|
||||||
{
|
{
|
||||||
/* Pass all entries <= i as FALSE, and the rest as MAYBE */
|
/* Pass all entries <= i as false, and the rest as MAYBE */
|
||||||
for (j = 0; j <= i; j++)
|
for (j = 0; j <= i; j++)
|
||||||
key->entryRes[entryIndexes[j]] = GIN_FALSE;
|
key->entryRes[entryIndexes[j]] = GIN_FALSE;
|
||||||
for (j = i + 1; j < key->nentries; j++)
|
for (j = i + 1; j < key->nentries; j++)
|
||||||
@ -633,6 +673,8 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry,
|
|||||||
entry->btree.fullScan = false;
|
entry->btree.fullScan = false;
|
||||||
stack = ginFindLeafPage(&entry->btree, true, snapshot);
|
stack = ginFindLeafPage(&entry->btree, true, snapshot);
|
||||||
|
|
||||||
|
GinPredicateLockPage(ginstate->index, BufferGetBlockNumber(stack->buffer), snapshot);
|
||||||
|
|
||||||
/* we don't need the stack, just the buffer. */
|
/* we don't need the stack, just the buffer. */
|
||||||
entry->buffer = stack->buffer;
|
entry->buffer = stack->buffer;
|
||||||
IncrBufferRefCount(entry->buffer);
|
IncrBufferRefCount(entry->buffer);
|
||||||
@ -677,6 +719,10 @@ entryLoadMoreItems(GinState *ginstate, GinScanEntry entry,
|
|||||||
entry->buffer = ginStepRight(entry->buffer,
|
entry->buffer = ginStepRight(entry->buffer,
|
||||||
ginstate->index,
|
ginstate->index,
|
||||||
GIN_SHARE);
|
GIN_SHARE);
|
||||||
|
|
||||||
|
GinPredicateLockPage(ginstate->index, BufferGetBlockNumber(entry->buffer), snapshot);
|
||||||
|
|
||||||
|
|
||||||
page = BufferGetPage(entry->buffer);
|
page = BufferGetPage(entry->buffer);
|
||||||
}
|
}
|
||||||
stepright = true;
|
stepright = true;
|
||||||
@ -1038,8 +1084,8 @@ keyGetItem(GinState *ginstate, MemoryContext tempCtx, GinScanKey key,
|
|||||||
* lossy page even when none of the other entries match.
|
* lossy page even when none of the other entries match.
|
||||||
*
|
*
|
||||||
* Our strategy is to call the tri-state consistent function, with the
|
* Our strategy is to call the tri-state consistent function, with the
|
||||||
* lossy-page entries set to MAYBE, and all the other entries FALSE. If it
|
* lossy-page entries set to MAYBE, and all the other entries false. If it
|
||||||
* returns FALSE, none of the lossy items alone are enough for a match, so
|
* returns false, none of the lossy items alone are enough for a match, so
|
||||||
* we don't need to return a lossy-page pointer. Otherwise, return a
|
* we don't need to return a lossy-page pointer. Otherwise, return a
|
||||||
* lossy-page pointer to indicate that the whole heap page must be
|
* lossy-page pointer to indicate that the whole heap page must be
|
||||||
* checked. (On subsequent calls, we'll do nothing until minItem is past
|
* checked. (On subsequent calls, we'll do nothing until minItem is past
|
||||||
@ -1700,7 +1746,8 @@ collectMatchesForHeapRow(IndexScanDesc scan, pendingPosition *pos)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Collect all matched rows from pending list into bitmap
|
* Collect all matched rows from pending list into bitmap. Also function
|
||||||
|
* takes PendingLockRelation if it's needed.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
|
scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
|
||||||
@ -1730,9 +1777,24 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
|
|||||||
{
|
{
|
||||||
/* No pending list, so proceed with normal scan */
|
/* No pending list, so proceed with normal scan */
|
||||||
UnlockReleaseBuffer(metabuffer);
|
UnlockReleaseBuffer(metabuffer);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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(scan->indexRelation))
|
||||||
|
PredicateLockRelation(scan->indexRelation, scan->xs_snapshot);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pending list is not empty, we need to lock the index doesn't despite on
|
||||||
|
* fastupdate state
|
||||||
|
*/
|
||||||
|
PredicateLockRelation(scan->indexRelation, scan->xs_snapshot);
|
||||||
|
|
||||||
pos.pendingBuffer = ReadBuffer(scan->indexRelation, blkno);
|
pos.pendingBuffer = ReadBuffer(scan->indexRelation, blkno);
|
||||||
LockBuffer(pos.pendingBuffer, GIN_SHARE);
|
LockBuffer(pos.pendingBuffer, GIN_SHARE);
|
||||||
pos.firstOffset = FirstOffsetNumber;
|
pos.firstOffset = FirstOffsetNumber;
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "storage/bufmgr.h"
|
#include "storage/bufmgr.h"
|
||||||
#include "storage/smgr.h"
|
#include "storage/smgr.h"
|
||||||
#include "storage/indexfsm.h"
|
#include "storage/indexfsm.h"
|
||||||
|
#include "storage/predicate.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
|
|
||||||
@ -48,7 +49,7 @@ static IndexTuple
|
|||||||
addItemPointersToLeafTuple(GinState *ginstate,
|
addItemPointersToLeafTuple(GinState *ginstate,
|
||||||
IndexTuple old,
|
IndexTuple old,
|
||||||
ItemPointerData *items, uint32 nitem,
|
ItemPointerData *items, uint32 nitem,
|
||||||
GinStatsData *buildStats)
|
GinStatsData *buildStats, Buffer buffer)
|
||||||
{
|
{
|
||||||
OffsetNumber attnum;
|
OffsetNumber attnum;
|
||||||
Datum key;
|
Datum key;
|
||||||
@ -99,7 +100,8 @@ addItemPointersToLeafTuple(GinState *ginstate,
|
|||||||
postingRoot = createPostingTree(ginstate->index,
|
postingRoot = createPostingTree(ginstate->index,
|
||||||
oldItems,
|
oldItems,
|
||||||
oldNPosting,
|
oldNPosting,
|
||||||
buildStats);
|
buildStats,
|
||||||
|
buffer);
|
||||||
|
|
||||||
/* Now insert the TIDs-to-be-added into the posting tree */
|
/* Now insert the TIDs-to-be-added into the posting tree */
|
||||||
ginInsertItemPointers(ginstate->index, postingRoot,
|
ginInsertItemPointers(ginstate->index, postingRoot,
|
||||||
@ -127,7 +129,7 @@ static IndexTuple
|
|||||||
buildFreshLeafTuple(GinState *ginstate,
|
buildFreshLeafTuple(GinState *ginstate,
|
||||||
OffsetNumber attnum, Datum key, GinNullCategory category,
|
OffsetNumber attnum, Datum key, GinNullCategory category,
|
||||||
ItemPointerData *items, uint32 nitem,
|
ItemPointerData *items, uint32 nitem,
|
||||||
GinStatsData *buildStats)
|
GinStatsData *buildStats, Buffer buffer)
|
||||||
{
|
{
|
||||||
IndexTuple res = NULL;
|
IndexTuple res = NULL;
|
||||||
GinPostingList *compressedList;
|
GinPostingList *compressedList;
|
||||||
@ -157,7 +159,7 @@ buildFreshLeafTuple(GinState *ginstate,
|
|||||||
* Initialize a new posting tree with the TIDs.
|
* Initialize a new posting tree with the TIDs.
|
||||||
*/
|
*/
|
||||||
postingRoot = createPostingTree(ginstate->index, items, nitem,
|
postingRoot = createPostingTree(ginstate->index, items, nitem,
|
||||||
buildStats);
|
buildStats, buffer);
|
||||||
|
|
||||||
/* And save the root link in the result tuple */
|
/* And save the root link in the result tuple */
|
||||||
GinSetPostingTree(res, postingRoot);
|
GinSetPostingTree(res, postingRoot);
|
||||||
@ -217,17 +219,19 @@ ginEntryInsert(GinState *ginstate,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GinCheckForSerializableConflictIn(btree.index, NULL, stack->buffer);
|
||||||
/* modify an existing leaf entry */
|
/* modify an existing leaf entry */
|
||||||
itup = addItemPointersToLeafTuple(ginstate, itup,
|
itup = addItemPointersToLeafTuple(ginstate, itup,
|
||||||
items, nitem, buildStats);
|
items, nitem, buildStats, stack->buffer);
|
||||||
|
|
||||||
insertdata.isDelete = true;
|
insertdata.isDelete = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
GinCheckForSerializableConflictIn(btree.index, NULL, stack->buffer);
|
||||||
/* no match, so construct a new leaf entry */
|
/* no match, so construct a new leaf entry */
|
||||||
itup = buildFreshLeafTuple(ginstate, attnum, key, category,
|
itup = buildFreshLeafTuple(ginstate, attnum, key, category,
|
||||||
items, nitem, buildStats);
|
items, nitem, buildStats, stack->buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Insert the new or modified leaf tuple */
|
/* Insert the new or modified leaf tuple */
|
||||||
@ -513,6 +517,18 @@ gininsert(Relation index, Datum *values, bool *isnull,
|
|||||||
|
|
||||||
memset(&collector, 0, sizeof(GinTupleCollector));
|
memset(&collector, 0, sizeof(GinTupleCollector));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* With fastupdate on each scan and each insert begin with access to
|
||||||
|
* pending list, so it effectively lock entire index. In this case
|
||||||
|
* we aquire predicate lock and check for conflicts over index relation,
|
||||||
|
* and hope that it will reduce locking overhead.
|
||||||
|
*
|
||||||
|
* Do not use GinCheckForSerializableConflictIn() here, because
|
||||||
|
* it will do nothing (it does actual work only with fastupdate off).
|
||||||
|
* Check for conflicts for entire index.
|
||||||
|
*/
|
||||||
|
CheckForSerializableConflictIn(index, NULL, InvalidBuffer);
|
||||||
|
|
||||||
for (i = 0; i < ginstate->origTupdesc->natts; i++)
|
for (i = 0; i < ginstate->origTupdesc->natts; i++)
|
||||||
ginHeapTupleFastCollect(ginstate, &collector,
|
ginHeapTupleFastCollect(ginstate, &collector,
|
||||||
(OffsetNumber) (i + 1),
|
(OffsetNumber) (i + 1),
|
||||||
@ -523,6 +539,16 @@ gininsert(Relation index, Datum *values, bool *isnull,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
GinStatsData stats;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fastupdate is off but if pending list isn't empty then we need to
|
||||||
|
* check conflicts with PredicateLockRelation in scanPendingInsert().
|
||||||
|
*/
|
||||||
|
ginGetStats(index, &stats);
|
||||||
|
if (stats.nPendingPages > 0)
|
||||||
|
CheckForSerializableConflictIn(index, NULL, InvalidBuffer);
|
||||||
|
|
||||||
for (i = 0; i < ginstate->origTupdesc->natts; i++)
|
for (i = 0; i < ginstate->origTupdesc->natts; i++)
|
||||||
ginHeapTupleInsert(ginstate, (OffsetNumber) (i + 1),
|
ginHeapTupleInsert(ginstate, (OffsetNumber) (i + 1),
|
||||||
values[i], isnull[i],
|
values[i], isnull[i],
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/indexfsm.h"
|
#include "storage/indexfsm.h"
|
||||||
#include "storage/lmgr.h"
|
#include "storage/lmgr.h"
|
||||||
|
#include "storage/predicate.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/index_selfuncs.h"
|
#include "utils/index_selfuncs.h"
|
||||||
#include "utils/typcache.h"
|
#include "utils/typcache.h"
|
||||||
@ -49,7 +50,7 @@ ginhandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->amsearchnulls = false;
|
amroutine->amsearchnulls = false;
|
||||||
amroutine->amstorage = true;
|
amroutine->amstorage = true;
|
||||||
amroutine->amclusterable = false;
|
amroutine->amclusterable = false;
|
||||||
amroutine->ampredlocks = false;
|
amroutine->ampredlocks = true;
|
||||||
amroutine->amcanparallel = false;
|
amroutine->amcanparallel = false;
|
||||||
amroutine->amkeytype = InvalidOid;
|
amroutine->amkeytype = InvalidOid;
|
||||||
|
|
||||||
@ -716,3 +717,10 @@ ginUpdateStats(Relation index, const GinStatsData *stats)
|
|||||||
|
|
||||||
END_CRIT_SECTION();
|
END_CRIT_SECTION();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GinCheckForSerializableConflictIn(Relation relation, HeapTuple tuple, Buffer buffer)
|
||||||
|
{
|
||||||
|
if (!GinGetUseFastUpdate(relation))
|
||||||
|
CheckForSerializableConflictIn(relation, tuple, buffer);
|
||||||
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "postmaster/autovacuum.h"
|
#include "postmaster/autovacuum.h"
|
||||||
#include "storage/indexfsm.h"
|
#include "storage/indexfsm.h"
|
||||||
#include "storage/lmgr.h"
|
#include "storage/lmgr.h"
|
||||||
|
#include "storage/predicate.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
|
|
||||||
struct GinVacuumState
|
struct GinVacuumState
|
||||||
@ -153,11 +154,18 @@ ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkn
|
|||||||
|
|
||||||
LockBuffer(lBuffer, GIN_EXCLUSIVE);
|
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();
|
START_CRIT_SECTION();
|
||||||
|
|
||||||
/* Unlink the page by changing left sibling's rightlink */
|
/* Unlink the page by changing left sibling's rightlink */
|
||||||
page = BufferGetPage(dBuffer);
|
|
||||||
rightlink = GinPageGetOpaque(page)->rightlink;
|
|
||||||
|
|
||||||
page = BufferGetPage(lBuffer);
|
page = BufferGetPage(lBuffer);
|
||||||
GinPageGetOpaque(page)->rightlink = rightlink;
|
GinPageGetOpaque(page)->rightlink = rightlink;
|
||||||
|
@ -380,6 +380,15 @@ then be trusted to ripple up to all levels and locations where
|
|||||||
conflicting predicate locks may exist. In case there is a page split,
|
conflicting predicate locks may exist. In case there is a page split,
|
||||||
we need to copy predicate lock from an original page to all new pages.
|
we need to copy predicate lock from an original page to all new pages.
|
||||||
|
|
||||||
|
* GIN searches acquire predicate locks only on the leaf pages
|
||||||
|
of entry tree and posting tree. During a page split, a predicate locks are
|
||||||
|
copied from the original page to the new page. In the same way predicate locks
|
||||||
|
are copied from entry tree leaf page to freshly created posting tree root.
|
||||||
|
However, when fast update is enabled, a predicate lock on the whole index
|
||||||
|
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.
|
||||||
|
|
||||||
* The effects of page splits, overflows, consolidations, and
|
* The effects of page splits, overflows, consolidations, and
|
||||||
removals must be carefully reviewed to ensure that predicate locks
|
removals must be carefully reviewed to ensure that predicate locks
|
||||||
aren't "lost" during those operations, or kept with pages which could
|
aren't "lost" during those operations, or kept with pages which could
|
||||||
|
@ -103,6 +103,8 @@ extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
|
|||||||
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
|
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
|
||||||
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
|
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
|
||||||
GinNullCategory *category);
|
GinNullCategory *category);
|
||||||
|
extern void GinCheckForSerializableConflictIn(Relation relation,
|
||||||
|
HeapTuple tuple, Buffer buffer);
|
||||||
|
|
||||||
/* gininsert.c */
|
/* gininsert.c */
|
||||||
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
|
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
|
||||||
@ -217,7 +219,7 @@ extern ItemPointer GinDataLeafPageGetItems(Page page, int *nitems, ItemPointerDa
|
|||||||
extern int GinDataLeafPageGetItemsToTbm(Page page, TIDBitmap *tbm);
|
extern int GinDataLeafPageGetItemsToTbm(Page page, TIDBitmap *tbm);
|
||||||
extern BlockNumber createPostingTree(Relation index,
|
extern BlockNumber createPostingTree(Relation index,
|
||||||
ItemPointerData *items, uint32 nitems,
|
ItemPointerData *items, uint32 nitems,
|
||||||
GinStatsData *buildStats);
|
GinStatsData *buildStats, Buffer entrybuffer);
|
||||||
extern void GinDataPageAddPostingItem(Page page, PostingItem *data, OffsetNumber offset);
|
extern void GinDataPageAddPostingItem(Page page, PostingItem *data, OffsetNumber offset);
|
||||||
extern void GinPageDeletePostingItem(Page page, OffsetNumber offset);
|
extern void GinPageDeletePostingItem(Page page, OffsetNumber offset);
|
||||||
extern void ginInsertItemPointers(Relation index, BlockNumber rootBlkno,
|
extern void ginInsertItemPointers(Relation index, BlockNumber rootBlkno,
|
||||||
|
756
src/test/isolation/expected/predicate-gin.out
Normal file
756
src/test/isolation/expected/predicate-gin.out
Normal file
@ -0,0 +1,756 @@
|
|||||||
|
Parsed test spec with 2 sessions
|
||||||
|
|
||||||
|
starting permutation: rxy1 wx1 c1 rxy2 wy2 c2
|
||||||
|
step rxy1: select count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) g;
|
||||||
|
step c1: commit;
|
||||||
|
step rxy2: select count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10050
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) g;
|
||||||
|
step c2: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy2 wy2 c2 rxy1 wx1 c1
|
||||||
|
step rxy2: select count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) g;
|
||||||
|
step c2: commit;
|
||||||
|
step rxy1: select count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10050
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) g;
|
||||||
|
step c1: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy3 wx3 c1 rxy4 wy4 c2
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step c1: commit;
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step c2: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy4 wy4 c2 rxy3 wx3 c1
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step c2: commit;
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step c1: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy1 wx1 rxy2 c1 wy2 c2
|
||||||
|
step rxy1: select count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) g;
|
||||||
|
step rxy2: select count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step c1: commit;
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) 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 count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) g;
|
||||||
|
step rxy2: select count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) 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 count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) g;
|
||||||
|
step rxy2: select count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) 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 count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step rxy2: select count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) g;
|
||||||
|
step c1: commit;
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) 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 count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step rxy2: select count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) g;
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) 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 count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step rxy2: select count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) g;
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) 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 count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step rxy2: select count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) g;
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) 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 count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step rxy2: select count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) g;
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) 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 count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step rxy2: select count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) g;
|
||||||
|
step c2: commit;
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) 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 count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step rxy1: select count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) g;
|
||||||
|
step c1: commit;
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) 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 count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step rxy1: select count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) g;
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) 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 count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step rxy1: select count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) g;
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) 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 count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step rxy1: select count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) g;
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) 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 count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step rxy1: select count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) g;
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) 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 count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step rxy1: select count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) g;
|
||||||
|
step c2: commit;
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) 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 count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) g;
|
||||||
|
step rxy1: select count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) 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 count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) g;
|
||||||
|
step rxy1: select count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) 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 count(*) from gin_tbl where p @> array[5,6];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step wy2: insert into gin_tbl select g, array[4,5] from
|
||||||
|
generate_series(20051, 20100) g;
|
||||||
|
step rxy1: select count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step c2: commit;
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) 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 count(*) from gin_tbl where p @> array[1,2] or
|
||||||
|
p @> array[100,200] or p @> array[500,1000] or p @> array[1000,2000];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) 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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step c1: commit;
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step c2: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy3 wx3 rxy4 wy4 c1 c2
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) 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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step c1: commit;
|
||||||
|
step c2: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy3 wx3 rxy4 wy4 c2 c1
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) 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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step c2: commit;
|
||||||
|
step c1: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy3 rxy4 wx3 c1 wy4 c2
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step c1: commit;
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step c2: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy3 rxy4 wx3 wy4 c1 c2
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step c1: commit;
|
||||||
|
step c2: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy3 rxy4 wx3 wy4 c2 c1
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step c2: commit;
|
||||||
|
step c1: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy3 rxy4 wy4 wx3 c1 c2
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step c1: commit;
|
||||||
|
step c2: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy3 rxy4 wy4 wx3 c2 c1
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step c2: commit;
|
||||||
|
step c1: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy3 rxy4 wy4 c2 wx3 c1
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step c2: commit;
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step c1: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy4 rxy3 wx3 c1 wy4 c2
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step c1: commit;
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step c2: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy4 rxy3 wx3 wy4 c1 c2
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step c1: commit;
|
||||||
|
step c2: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy4 rxy3 wx3 wy4 c2 c1
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step c2: commit;
|
||||||
|
step c1: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy4 rxy3 wy4 wx3 c1 c2
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step c1: commit;
|
||||||
|
step c2: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy4 rxy3 wy4 wx3 c2 c1
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step c2: commit;
|
||||||
|
step c1: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy4 rxy3 wy4 c2 wx3 c1
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) g;
|
||||||
|
step c2: commit;
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step c1: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy4 wy4 rxy3 wx3 c1 c2
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) 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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step c1: commit;
|
||||||
|
step c2: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy4 wy4 rxy3 wx3 c2 c1
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) 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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step c2: commit;
|
||||||
|
step c1: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy4 wy4 rxy3 c2 wx3 c1
|
||||||
|
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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step wy4: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(10000, 10050) 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];
|
||||||
|
count
|
||||||
|
|
||||||
|
4
|
||||||
|
step c2: commit;
|
||||||
|
step wx3: insert into gin_tbl select g, array[g,g*2] from generate_series
|
||||||
|
(1, 50) g;
|
||||||
|
step c1: commit;
|
||||||
|
|
||||||
|
starting permutation: rxy1 rxy2fu wx1 c1 wy2fu c2
|
||||||
|
step rxy1: select count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step rxy2fu: select count(*) from gin_tbl where p @> array[10000,10005];
|
||||||
|
count
|
||||||
|
|
||||||
|
0
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) g;
|
||||||
|
step c1: commit;
|
||||||
|
step wy2fu: insert into gin_tbl select g, array[10000,10005] from
|
||||||
|
generate_series(20051, 20100) g;
|
||||||
|
step c2: commit;
|
||||||
|
|
||||||
|
starting permutation: fu1 rxy1 rxy2fu wx1 c1 wy2fu c2
|
||||||
|
step fu1: alter index ginidx set (fastupdate = on);
|
||||||
|
commit;
|
||||||
|
begin isolation level serializable;
|
||||||
|
set enable_seqscan=off;
|
||||||
|
step rxy1: select count(*) from gin_tbl where p @> array[4,5];
|
||||||
|
count
|
||||||
|
|
||||||
|
10000
|
||||||
|
step rxy2fu: select count(*) from gin_tbl where p @> array[10000,10005];
|
||||||
|
count
|
||||||
|
|
||||||
|
0
|
||||||
|
step wx1: insert into gin_tbl select g, array[5,6] from generate_series
|
||||||
|
(20001, 20050) g;
|
||||||
|
step c1: commit;
|
||||||
|
step wy2fu: insert into gin_tbl select g, array[10000,10005] from
|
||||||
|
generate_series(20051, 20100) g;
|
||||||
|
ERROR: could not serialize access due to read/write dependencies among transactions
|
||||||
|
step c2: commit;
|
@ -67,3 +67,4 @@ test: vacuum-reltuples
|
|||||||
test: timeouts
|
test: timeouts
|
||||||
test: vacuum-concurrent-drop
|
test: vacuum-concurrent-drop
|
||||||
test: predicate-gist
|
test: predicate-gist
|
||||||
|
test: predicate-gin
|
||||||
|
134
src/test/isolation/specs/predicate-gin.spec
Normal file
134
src/test/isolation/specs/predicate-gin.spec
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
# 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;
|
||||||
|
begin isolation level serializable;
|
||||||
|
set enable_seqscan=off; }
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user