Skip to content

Commit 19cf52e

Browse files
committed
Prevent deadlock in ginRedoDeletePage()
On standby ginRedoDeletePage() can work concurrently with read-only queries. Those queries can traverse posting tree in two ways. 1) Using rightlinks by ginStepRight(), which locks the next page before unlocking its left sibling. 2) Using downlinks by ginFindLeafPage(), which locks at most one page at time. Original lock order was: page, parent, left sibling. That lock order can deadlock with ginStepRight(). In order to prevent deadlock this commit changes lock order to: left sibling, page, parent. Note, that position of parent in locking order seems insignificant, because we only lock one page at time while traversing downlinks. Reported-by: Chen Huajun Diagnosed-by: Chen Huajun, Peter Geoghegan, Andrey Borodin Discussion: https://postgr.es/m/31a702a.14dd.166c1366ac1.Coremail.chjischj%40163.com Author: Alexander Korotkov Backpatch-through: 9.4
1 parent a967229 commit 19cf52e

File tree

1 file changed

+25
-19
lines changed

1 file changed

+25
-19
lines changed

src/backend/access/gin/ginxlog.c

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,29 @@ ginRedoDeletePage(XLogRecPtr lsn, XLogRecord *record)
693693
Buffer lbuffer;
694694
Page page;
695695

696+
/*
697+
* Lock left page first in order to prevent possible deadlock with
698+
* ginStepRight().
699+
*/
700+
if (record->xl_info & XLR_BKP_BLOCK(2))
701+
(void) RestoreBackupBlock(lsn, record, 2, false, false);
702+
else if (data->leftBlkno != InvalidBlockNumber)
703+
{
704+
lbuffer = XLogReadBuffer(data->node, data->leftBlkno, false);
705+
if (BufferIsValid(lbuffer))
706+
{
707+
page = BufferGetPage(lbuffer);
708+
if (lsn > PageGetLSN(page))
709+
{
710+
Assert(GinPageIsData(page));
711+
GinPageGetOpaque(page)->rightlink = data->rightLink;
712+
PageSetLSN(page, lsn);
713+
MarkBufferDirty(lbuffer);
714+
}
715+
UnlockReleaseBuffer(lbuffer);
716+
}
717+
}
718+
696719
if (record->xl_info & XLR_BKP_BLOCK(0))
697720
dbuffer = RestoreBackupBlock(lsn, record, 0, false, true);
698721
else
@@ -730,25 +753,8 @@ ginRedoDeletePage(XLogRecPtr lsn, XLogRecord *record)
730753
}
731754
}
732755

733-
if (record->xl_info & XLR_BKP_BLOCK(2))
734-
(void) RestoreBackupBlock(lsn, record, 2, false, false);
735-
else if (data->leftBlkno != InvalidBlockNumber)
736-
{
737-
lbuffer = XLogReadBuffer(data->node, data->leftBlkno, false);
738-
if (BufferIsValid(lbuffer))
739-
{
740-
page = BufferGetPage(lbuffer);
741-
if (lsn > PageGetLSN(page))
742-
{
743-
Assert(GinPageIsData(page));
744-
GinPageGetOpaque(page)->rightlink = data->rightLink;
745-
PageSetLSN(page, lsn);
746-
MarkBufferDirty(lbuffer);
747-
}
748-
UnlockReleaseBuffer(lbuffer);
749-
}
750-
}
751-
756+
if (BufferIsValid(lbuffer))
757+
UnlockReleaseBuffer(lbuffer);
752758
if (BufferIsValid(pbuffer))
753759
UnlockReleaseBuffer(pbuffer);
754760
if (BufferIsValid(dbuffer))

0 commit comments

Comments
 (0)