From: Alexander Korotkov Date: Tue, 28 Jul 2026 08:50:13 +0000 (+0200) Subject: Restore vacuum_delay_point() in GIN posting-tree leaf vacuum X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;p=thirdparty%2Fpostgresql.git Restore vacuum_delay_point() in GIN posting-tree leaf vacuum Commit fd83c83d094 turned the recursive posting-tree cleanup in ginVacuumPostingTreeLeaves() into an iterative sweep that follows the tree's leaf pages via their rightlinks. The recursive version called vacuum_delay_point() while processing the tree, but that call was removed and never re-added to the new loop. As that commit only set out to fix a deadlock, the removal appears to have been unintentional. Consequently the leaf-page sweep of a single posting tree runs with no vacuum_delay_point(), and therefore no CHECK_FOR_INTERRUPTS(). A posting tree stores all the TIDs for one indexed key, so for a frequently occurring key it can span a large number of leaf pages. While such a tree is being vacuumed the operation ignores vacuum_cost_delay and does not respond to query cancellation or statement_timeout; an autovacuum worker likewise cannot be interrupted mid-sweep when another backend requests a conflicting lock. Restore the call, placed after the current page has been unlocked and released so that no buffer content lock is held across a potential delay (cf. 21c27af65fb). The sibling loops in ginbulkdelete() and ginvacuumcleanup() already call vacuum_delay_point() once per page. Author: Paul Kim Co-authored-by: Alexander Korotkov Reviewed-by: Michael Paquier Reviewed-by: Andrey Borodin Reviewed-by: solai v Discussion: https://postgr.es/m/178447127453.110.12276981925360691905%40mail.gmail.com Backpatch-through: 14 --- diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c index 840543eb664..040f21a92e3 100644 --- a/src/backend/access/gin/ginvacuum.c +++ b/src/backend/access/gin/ginvacuum.c @@ -429,6 +429,13 @@ ginVacuumPostingTreeLeaves(GinVacuumState *gvs, BlockNumber blkno) if (blkno == InvalidBlockNumber) break; + /* + * A safe point to delay/accept interrupts: the previous page has been + * unlocked and released, so we hold no buffer content lock (nor any + * other LWLock) here and CHECK_FOR_INTERRUPTS() can do its job. + */ + vacuum_delay_point(false); + buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno, RBM_NORMAL, gvs->strategy); LockBuffer(buffer, GIN_EXCLUSIVE);