Updated the patch after a rebase to match up to 8637383f5ff and for CFBot.
Thinking about backpatching: This is an improvement instead of a bugfix or a
regression, so I'm proposing it for master. But I'm happy to be overruled, and
there's no technical reason it couldn't go back as far as 14.
- Kevin Rocker
From 96334df954801d17750ea8d0810c1a7caeec173a Mon Sep 17 00:00:00 2001
From: Kevin Rocker <[email protected]>
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