From 187e72ff3c69ffb0a17f496b4eb3add64958cdbb Mon Sep 17 00:00:00 2001
From: Kevin Rocker <me@kevinrocker.com>
Date: Fri, 31 Jul 2026 23:34:10 +0200
Subject: [PATCH v1] Use the vacuum buffer access strategy in GIN pending-list
 cleanup.

When ginInsertCleanup() is reached from VACUUM, it reads the metapage
and every pending-list page with ReadBuffer(), ignoring
BufferAccessStrategy from the vacuum machinery.  The same was true of
shiftList(), which re-reads the processed pages before deleting them.

Pass the strategy down from the three vacuum-side callers.  The
post-insert cleanup path and gin_clean_pending_list() pass NULL,
preserving current behavior.
---
 src/backend/access/gin/ginfast.c   | 29 ++++++++++++++++++++---------
 src/backend/access/gin/ginvacuum.c |  7 ++++---
 src/include/access/gin_private.h   |  4 +++-
 3 files changed, 27 insertions(+), 13 deletions(-)

diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c
index f50848eb65a..6fb3180c4f8 100644
--- a/src/backend/access/gin/ginfast.c
+++ b/src/backend/access/gin/ginfast.c
@@ -468,7 +468,7 @@ ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
 	 * pending list not forcibly.
 	 */
 	if (needCleanup)
-		ginInsertCleanup(ginstate, false, true, false, NULL);
+		ginInsertCleanup(ginstate, false, true, false, NULL, NULL);
 }
 
 /*
@@ -552,7 +552,8 @@ ginHeapTupleFastCollect(GinState *ginstate,
  */
 static void
 shiftList(Relation index, Buffer metabuffer, BlockNumber newHead,
-		  bool fill_fsm, IndexBulkDeleteResult *stats)
+		  bool fill_fsm, IndexBulkDeleteResult *stats,
+		  BufferAccessStrategy strategy)
 {
 	Page		metapage;
 	GinMetaPageData *metadata;
@@ -575,7 +576,9 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead,
 		while (data.ndeleted < GIN_NDELETE_AT_ONCE && blknoToDelete != newHead)
 		{
 			freespace[data.ndeleted] = blknoToDelete;
-			buffers[data.ndeleted] = ReadBuffer(index, blknoToDelete);
+			buffers[data.ndeleted] = ReadBufferExtended(index, MAIN_FORKNUM,
+														blknoToDelete,
+														RBM_NORMAL, strategy);
 			LockBuffer(buffers[data.ndeleted], GIN_EXCLUSIVE);
 			page = BufferGetPage(buffers[data.ndeleted]);
 
@@ -775,11 +778,16 @@ processPendingPage(BuildAccumulator *accum, KeyArray *ka,
  * FSM.
  *
  * If stats isn't null, we count deleted pending pages into the counts.
+ *
+ * If strategy isn't null, use that buffer access strategy to read the
+ * pending-list pages; vacuum passes its strategy so that the cleanup
+ * doesn't disturb the shared buffer cache more than necessary.
  */
 void
 ginInsertCleanup(GinState *ginstate, bool full_clean,
 				 bool fill_fsm, bool forceCleanup,
-				 IndexBulkDeleteResult *stats)
+				 IndexBulkDeleteResult *stats,
+				 BufferAccessStrategy strategy)
 {
 	Relation	index = ginstate->index;
 	Buffer		metabuffer,
@@ -827,7 +835,8 @@ ginInsertCleanup(GinState *ginstate, bool full_clean,
 		workMemory = work_mem;
 	}
 
-	metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
+	metabuffer = ReadBufferExtended(index, MAIN_FORKNUM, GIN_METAPAGE_BLKNO,
+									RBM_NORMAL, strategy);
 	LockBuffer(metabuffer, GIN_SHARE);
 	metapage = BufferGetPage(metabuffer);
 	metadata = GinPageGetMeta(metapage);
@@ -850,7 +859,8 @@ ginInsertCleanup(GinState *ginstate, bool full_clean,
 	 * Read and lock head of pending list
 	 */
 	blkno = metadata->head;
-	buffer = ReadBuffer(index, blkno);
+	buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+								RBM_NORMAL, strategy);
 	LockBuffer(buffer, GIN_SHARE);
 	page = BufferGetPage(buffer);
 
@@ -971,7 +981,7 @@ ginInsertCleanup(GinState *ginstate, bool full_clean,
 			 * remove read pages from pending list, at this point all content
 			 * of read pages is in regular structure
 			 */
-			shiftList(index, metabuffer, blkno, fill_fsm, stats);
+			shiftList(index, metabuffer, blkno, fill_fsm, stats, strategy);
 
 			/* At this point, some pending pages have been freed up */
 			fsm_vac = true;
@@ -1003,7 +1013,8 @@ ginInsertCleanup(GinState *ginstate, bool full_clean,
 		 * Read next page in pending list
 		 */
 		vacuum_delay_point(false);
-		buffer = ReadBuffer(index, blkno);
+		buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
+									RBM_NORMAL, strategy);
 		LockBuffer(buffer, GIN_SHARE);
 		page = BufferGetPage(buffer);
 	}
@@ -1077,7 +1088,7 @@ gin_clean_pending_list(PG_FUNCTION_ARGS)
 		GinState	ginstate;
 
 		initGinState(&ginstate, indexRel);
-		ginInsertCleanup(&ginstate, true, true, true, &stats);
+		ginInsertCleanup(&ginstate, true, true, true, &stats, NULL);
 	}
 	else
 		ereport(DEBUG1,
diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c
index 040f21a92e3..e8baf419674 100644
--- a/src/backend/access/gin/ginvacuum.c
+++ b/src/backend/access/gin/ginvacuum.c
@@ -635,7 +635,7 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 		 * and cleanup any pending inserts
 		 */
 		ginInsertCleanup(&gvs.ginstate, !AmAutoVacuumWorkerProcess(),
-						 false, true, stats);
+						 false, true, stats, info->strategy);
 	}
 
 	/* we'll re-count the tuples each time */
@@ -750,7 +750,8 @@ ginvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
 		if (AmAutoVacuumWorkerProcess())
 		{
 			initGinState(&ginstate, index);
-			ginInsertCleanup(&ginstate, false, true, true, stats);
+			ginInsertCleanup(&ginstate, false, true, true, stats,
+							 info->strategy);
 		}
 		return stats;
 	}
@@ -764,7 +765,7 @@ ginvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
 		stats = palloc0_object(IndexBulkDeleteResult);
 		initGinState(&ginstate, index);
 		ginInsertCleanup(&ginstate, !AmAutoVacuumWorkerProcess(),
-						 false, true, stats);
+						 false, true, stats, info->strategy);
 	}
 
 	memset(&idxStat, 0, sizeof(idxStat));
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 6725ee2839f..7319242cd16 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -469,7 +469,9 @@ extern void ginHeapTupleFastCollect(GinState *ginstate,
 									OffsetNumber attnum, Datum value, bool isNull,
 									ItemPointer ht_ctid);
 extern void ginInsertCleanup(GinState *ginstate, bool full_clean,
-							 bool fill_fsm, bool forceCleanup, IndexBulkDeleteResult *stats);
+							 bool fill_fsm, bool forceCleanup,
+							 IndexBulkDeleteResult *stats,
+							 BufferAccessStrategy strategy);
 
 /* ginpostinglist.c */
 
-- 
2.54.0

