ginInsertCleanup() calls vacuum_delay_point while still holding a lock on the 
current pending-list page, so the delay runs with interrupts held off.

This is the same problem 2d7f6947293 fixed in btbulkdelete() back in 2006, and 
it's related to 8a045f760f6 which went in last week.

The patch keeps the other vacuum_delay_point() calls that exist after the 
buffer is released, in both the flush branch and the main branch at the end of 
the loop.

I haven't seen this hang directly and this is my first contribution, so I'd 
appreciate a second opinion on anything. The issue exists in multiple supported 
branches, but should be easy to backpatch (happy to help with that).

The issue was surfaced by Opus, I've verified the logic against those two 
commits myself.

- Kevin Rocker
From c774fc30710a7d9af142f75f079759f2275e951e Mon Sep 17 00:00:00 2001
From: Kevin Rocker <[email protected]>
Date: Fri, 31 Jul 2026 23:32:17 +0200
Subject: [PATCH v1 1/4] Don't call vacuum_delay_point() while holding a buffer
 lock in GIN.

ginInsertCleanup() called vacuum_delay_point() while still holding a shared
content lock on the current pending-list page.

Every iteration of the loop still passes a vacuum_delay_point() without
a lock: the flush branch unlocks the page before sleeping inside the
ginEntryInsert() loop, and both branches reach a delay after releasing
the buffer and before the next iteration.

This is the same fix commit 2d7f6947293 made in btbulkdelete();
cf. also 8a045f760f6 for the placement rule in GIN's posting-tree vacuum.
---
 src/backend/access/gin/ginfast.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c
index f50848eb65a..120f0705da0 100644
--- a/src/backend/access/gin/ginfast.c
+++ b/src/backend/access/gin/ginfast.c
@@ -873,6 +873,9 @@ ginInsertCleanup(GinState *ginstate, bool full_clean,
 	 * At the top of this loop, we have pin and lock on the current page of
 	 * the pending list.  However, we'll release that before exiting the loop.
 	 * Note we also have pin but not lock on the metapage.
+	 *
+	 * The vacuum_delay_point() calls below are placed where
+	 * the current page is not locked.
 	 */
 	for (;;)
 	{
@@ -892,8 +895,6 @@ ginInsertCleanup(GinState *ginstate, bool full_clean,
 		 */
 		processPendingPage(&accum, &datums, page, FirstOffsetNumber);
 
-		vacuum_delay_point(false);
-
 		/*
 		 * Is it time to flush memory to disk?	Flush if we are at the end of
 		 * the pending list, or if we have a full row and memory is getting
-- 
2.54.0

Reply via email to