Skip to content

Commit

Permalink
Predicate locking in gin index
Browse files Browse the repository at this point in the history
  • Loading branch information
shubhambaraiss committed Aug 7, 2017
1 parent 119fdd8 commit 6172639
Show file tree
Hide file tree
Showing 11 changed files with 816 additions and 3 deletions.
14 changes: 14 additions & 0 deletions src/backend/access/gin/ginbtree.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -515,6 +516,15 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
btree->fillRoot(btree, newrootpg,
BufferGetBlockNumber(lbuffer), newlpage,
BufferGetBlockNumber(rbuffer), newrpage);

PredicateLockPageSplit(btree->index,
BufferGetBlockNumber(stack->buffer),
BufferGetBlockNumber(lbuffer));

PredicateLockPageSplit(btree->index,
BufferGetBlockNumber(stack->buffer),
BufferGetBlockNumber(rbuffer));

}
else
{
Expand All @@ -524,6 +534,10 @@ ginPlaceToPage(GinBtree btree, GinBtreeStack *stack,
GinPageGetOpaque(newrpage)->rightlink = savedRightLink;
GinPageGetOpaque(newlpage)->flags |= GIN_INCOMPLETE_SPLIT;
GinPageGetOpaque(newlpage)->rightlink = BufferGetBlockNumber(rbuffer);

PredicateLockPageSplit(btree->index,
BufferGetBlockNumber(stack->buffer),
BufferGetBlockNumber(rbuffer));
}

/*
Expand Down
38 changes: 38 additions & 0 deletions src/backend/access/gin/ginget.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -73,6 +75,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);
Expand All @@ -94,6 +100,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);
Expand Down Expand Up @@ -323,6 +332,17 @@ startScanEntry(GinState *ginstate, GinScanEntry entry, Snapshot snapshot)
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);
else
PredicateLockPage(ginstate->index, BufferGetBlockNumber(stackEntry->buffer), snapshot);

/* ginFindLeafPage() will have already checked snapshot age. */
needUnlock = TRUE;

Expand Down Expand Up @@ -391,6 +411,9 @@ startScanEntry(GinState *ginstate, GinScanEntry entry, Snapshot snapshot)
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
Expand Down Expand Up @@ -633,6 +656,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);
Expand Down Expand Up @@ -677,6 +703,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;
Expand Down Expand Up @@ -1733,6 +1764,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;
Expand Down
5 changes: 5 additions & 0 deletions src/backend/access/gin/gininsert.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "access/gin_private.h"
#include "access/ginxlog.h"
#include "access/xloginsert.h"
#include "storage/predicate.h"
#include "catalog/index.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
Expand Down Expand Up @@ -196,6 +197,8 @@ ginEntryInsert(GinState *ginstate,
stack = ginFindLeafPage(&btree, false, NULL);
page = BufferGetPage(stack->buffer);

CheckForSerializableConflictIn(btree.index, NULL, stack->buffer);

if (btree.findItem(&btree, stack))
{
/* found pre-existing entry */
Expand Down Expand Up @@ -513,6 +516,8 @@ gininsert(Relation index, Datum *values, bool *isnull,

memset(&collector, 0, sizeof(GinTupleCollector));

CheckForSerializableConflictIn(index, NULL, NULL);

for (i = 0; i < ginstate->origTupdesc->natts; i++)
ginHeapTupleFastCollect(ginstate, &collector,
(OffsetNumber) (i + 1),
Expand Down
2 changes: 1 addition & 1 deletion src/backend/access/gin/ginutil.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
11 changes: 9 additions & 2 deletions src/backend/access/gin/ginvacuum.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,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;
Expand Down
5 changes: 5 additions & 0 deletions src/backend/storage/lmgr/README-SSI
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,11 @@ 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.
If, however, fast update is enabled, a predicate lock on the index
relation is required. 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
Expand Down
Loading

0 comments on commit 6172639

Please sign in to comment.