From 96334df954801d17750ea8d0810c1a7caeec173a Mon Sep 17 00:00:00 2001
From: Kevin Rocker <me@kevinrocker.com>
Date: Fri, 31 Jul 2026 23:34:10 +0200
Subject: [PATCH v2] 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 46fc60115a8..f74a44db720 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 must_empty_list,
 				 bool fill_fsm, bool forceCleanup,
-				 IndexBulkDeleteResult *stats)
+				 IndexBulkDeleteResult *stats,
+				 BufferAccessStrategy strategy)
 {
 	Relation	index = ginstate->index;
 	Buffer		metabuffer,
@@ -829,7 +837,8 @@ ginInsertCleanup(GinState *ginstate, bool must_empty_list,
 		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);
@@ -852,7 +861,8 @@ ginInsertCleanup(GinState *ginstate, bool must_empty_list,
 	 * 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);
 
@@ -974,7 +984,7 @@ ginInsertCleanup(GinState *ginstate, bool must_empty_list,
 			 * 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;
@@ -1006,7 +1016,8 @@ ginInsertCleanup(GinState *ginstate, bool must_empty_list,
 		 * 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);
 	}
@@ -1080,7 +1091,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 d69d59748b5..c3ea1fbb15f 100644
--- a/src/backend/access/gin/ginvacuum.c
+++ b/src/backend/access/gin/ginvacuum.c
@@ -652,7 +652,7 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 	 * cannot insert new tuples whose TIDs VACUUM needs us to remove.
 	 */
 	ginInsertCleanup(&gvs.ginstate, !AmAutoVacuumWorkerProcess(),
-					 false, true, stats);
+					 false, true, stats, info->strategy);
 
 	/* we'll re-count the tuples each time */
 	stats->num_index_tuples = 0;
@@ -766,7 +766,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;
 	}
@@ -780,7 +781,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 3c5fd6ba817..4b954537073 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 must_empty_list,
-							 bool fill_fsm, bool forceCleanup, IndexBulkDeleteResult *stats);
+							 bool fill_fsm, bool forceCleanup,
+							 IndexBulkDeleteResult *stats,
+							 BufferAccessStrategy strategy);
 
 /* ginpostinglist.c */
 
-- 
2.54.0

