diff options
Diffstat (limited to 'src/backend/access/gin/ginvacuum.c')
-rw-r--r-- | src/backend/access/gin/ginvacuum.c | 54 |
1 files changed, 35 insertions, 19 deletions
diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c index dc46f2460e2..85acddc76b3 100644 --- a/src/backend/access/gin/ginvacuum.c +++ b/src/backend/access/gin/ginvacuum.c @@ -117,7 +117,8 @@ typedef struct DataPageDeleteStack struct DataPageDeleteStack *parent; BlockNumber blkno; /* current block number */ - BlockNumber leftBlkno; /* rightest non-deleted page on left */ + Buffer leftBuffer; /* pinned and locked rightest non-deleted page + * on left */ bool isRoot; } DataPageDeleteStack; @@ -139,11 +140,8 @@ ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkn /* * This function MUST be called only if someone of parent pages hold * exclusive cleanup lock. This guarantees that no insertions currently - * happen in this subtree. Caller also acquire Exclusive lock on deletable - * page and is acquiring and releasing exclusive lock on left page before. - * Left page was locked and released. Then parent and this page are - * locked. We acquire left page lock here only to mark page dirty after - * changing right pointer. + * happen in this subtree. Caller also acquires Exclusive locks on + * deletable, parent and left pages. */ lBuffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, leftBlkno, RBM_NORMAL, gvs->strategy); @@ -152,8 +150,6 @@ ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkn pBuffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, parentBlkno, RBM_NORMAL, gvs->strategy); - LockBuffer(lBuffer, GIN_EXCLUSIVE); - page = BufferGetPage(dBuffer); rightlink = GinPageGetOpaque(page)->rightlink; @@ -227,7 +223,7 @@ ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkn } ReleaseBuffer(pBuffer); - UnlockReleaseBuffer(lBuffer); + ReleaseBuffer(lBuffer); ReleaseBuffer(dBuffer); END_CRIT_SECTION(); @@ -237,7 +233,11 @@ ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkn /* - * scans posting tree and deletes empty pages + * Scans posting tree and deletes empty pages. Caller must lock root page for + * cleanup. During scan path from root to current page is kept exclusively + * locked. Also keep left page exclusively locked, because ginDeletePage() + * needs it. If we try to relock left page later, it could deadlock with + * ginStepRight(). */ static bool ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, @@ -260,7 +260,7 @@ ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, me = (DataPageDeleteStack *) palloc0(sizeof(DataPageDeleteStack)); me->parent = parent; parent->child = me; - me->leftBlkno = InvalidBlockNumber; + me->leftBuffer = InvalidBuffer; } else me = parent->child; @@ -288,6 +288,12 @@ ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, if (ginScanToDelete(gvs, PostingItemGetBlockNumber(pitem), false, me, i)) i--; } + + if (GinPageRightMost(page) && BufferIsValid(me->child->leftBuffer)) + { + UnlockReleaseBuffer(me->child->leftBuffer); + me->child->leftBuffer = InvalidBuffer; + } } if (GinPageIsLeaf(page)) @@ -298,21 +304,31 @@ ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, if (isempty) { /* we never delete the left- or rightmost branch */ - if (me->leftBlkno != InvalidBlockNumber && !GinPageRightMost(page)) + if (BufferIsValid(me->leftBuffer) && !GinPageRightMost(page)) { Assert(!isRoot); - ginDeletePage(gvs, blkno, me->leftBlkno, me->parent->blkno, myoff, me->parent->isRoot); + ginDeletePage(gvs, blkno, BufferGetBlockNumber(me->leftBuffer), + me->parent->blkno, myoff, me->parent->isRoot); meDelete = true; } } - if (!isRoot) - LockBuffer(buffer, GIN_UNLOCK); + if (!meDelete) + { + if (BufferIsValid(me->leftBuffer)) + UnlockReleaseBuffer(me->leftBuffer); + me->leftBuffer = buffer; + } + else + { + if (!isRoot) + LockBuffer(buffer, GIN_UNLOCK); - ReleaseBuffer(buffer); + ReleaseBuffer(buffer); + } - if (!meDelete) - me->leftBlkno = blkno; + if (isRoot) + ReleaseBuffer(buffer); return meDelete; } @@ -409,7 +425,7 @@ ginVacuumPostingTree(GinVacuumState *gvs, BlockNumber rootBlkno) LockBufferForCleanup(buffer); memset(&root, 0, sizeof(DataPageDeleteStack)); - root.leftBlkno = InvalidBlockNumber; + root.leftBuffer = InvalidBuffer; root.isRoot = true; ginScanToDelete(gvs, rootBlkno, true, &root, InvalidOffsetNumber); |