From e693adff50f09fe791c87a444950b193b642aabb Mon Sep 17 00:00:00 2001 From: Masahiko Sawada Date: Mon, 21 Apr 2025 22:41:33 -0700 Subject: [PATCH v1 2/3] Convert IndexBulkDeleteCallback to process TIDs in batch. Author: Reviewed-by: Discussion: https://postgr.es/m/ Backpatch-through: --- contrib/bloom/blvacuum.c | 4 +++- src/backend/access/gin/ginvacuum.c | 3 ++- src/backend/access/gist/gistvacuum.c | 3 ++- src/backend/access/hash/hash.c | 3 ++- src/backend/access/nbtree/nbtree.c | 4 ++-- src/backend/access/spgist/spgvacuum.c | 13 ++++++++----- src/backend/catalog/index.c | 21 ++++++++++++++------- src/backend/commands/vacuum.c | 8 ++++---- src/include/access/genam.h | 3 ++- 9 files changed, 39 insertions(+), 23 deletions(-) diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c index 86b15a75f6f..93089456855 100644 --- a/contrib/bloom/blvacuum.c +++ b/contrib/bloom/blvacuum.c @@ -83,8 +83,10 @@ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, OffsetNumberNext(BloomPageGetMaxOffset(page))); while (itup < itupEnd) { + bool dead; + /* Do we have to delete this tuple? */ - if (callback(&itup->heapPtr, callback_state)) + if (callback(&itup->heapPtr, 1, &dead, callback_state) > 0) { /* Yes; adjust count of tuples that will be left on page */ BloomPageGetOpaque(page)->maxoff--; diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c index fbbe3a6dd70..336f100261b 100644 --- a/src/backend/access/gin/ginvacuum.c +++ b/src/backend/access/gin/ginvacuum.c @@ -56,7 +56,8 @@ ginVacuumItemPointers(GinVacuumState *gvs, ItemPointerData *items, */ for (i = 0; i < nitem; i++) { - if (gvs->callback(items + i, gvs->callback_state)) + bool deletable; + if (gvs->callback(items + i, 1, &deletable, gvs->callback_state) > 0) { gvs->result->tuples_removed += 1; if (!tmpitems) diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c index 6a359c98c60..0abefb7b664 100644 --- a/src/backend/access/gist/gistvacuum.c +++ b/src/backend/access/gist/gistvacuum.c @@ -383,8 +383,9 @@ restart: { ItemId iid = PageGetItemId(page, off); IndexTuple idxtuple = (IndexTuple) PageGetItem(page, iid); + bool deletable; - if (callback(&(idxtuple->t_tid), callback_state)) + if (callback(&(idxtuple->t_tid), 1, &deletable, callback_state) > 0) todelete[ntodelete++] = off; } } diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c index 53061c819fb..e0fffadf698 100644 --- a/src/backend/access/hash/hash.c +++ b/src/backend/access/hash/hash.c @@ -734,6 +734,7 @@ hashbucketcleanup(Relation rel, Bucket cur_bucket, Buffer bucket_buf, IndexTuple itup; Bucket bucket; bool kill_tuple = false; + bool dead; itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offno)); @@ -743,7 +744,7 @@ hashbucketcleanup(Relation rel, Bucket cur_bucket, Buffer bucket_buf, * To remove the dead tuples, we strictly want to rely on results * of callback function. refer btvacuumpage for detailed reason. */ - if (callback && callback(htup, callback_state)) + if (callback && callback(htup, 1, &dead, callback_state) > 0) { kill_tuple = true; if (tuples_removed) diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c index accc7fe8bbe..821e16d5691 100644 --- a/src/backend/access/nbtree/nbtree.c +++ b/src/backend/access/nbtree/nbtree.c @@ -1486,7 +1486,7 @@ backtrack: if (!BTreeTupleIsPosting(itup)) { /* Regular tuple, standard table TID representation */ - if (callback(&itup->t_tid, callback_state)) + if (callback(&itup->t_tid, 1, NULL, callback_state) > 0) { deletable[ndeletable++] = offnum; nhtidsdead++; @@ -1670,7 +1670,7 @@ btreevacuumposting(BTVacState *vstate, IndexTuple posting, for (int i = 0; i < nitem; i++) { - if (!vstate->callback(items + i, vstate->callback_state)) + if (vstate->callback(items + i, 1, NULL, vstate->callback_state) == 0) { /* Live table TID */ live++; diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c index 81171f35451..a9a61fa152e 100644 --- a/src/backend/access/spgist/spgvacuum.c +++ b/src/backend/access/spgist/spgvacuum.c @@ -153,9 +153,11 @@ vacuumLeafPage(spgBulkDeleteState *bds, Relation index, Buffer buffer, PageGetItemId(page, i)); if (lt->tupstate == SPGIST_LIVE) { + bool dead; + Assert(ItemPointerIsValid(<->heapPtr)); - if (bds->callback(<->heapPtr, bds->callback_state)) + if (bds->callback(<->heapPtr, 1, &dead, bds->callback_state) > 0) { bds->stats->tuples_removed += 1; deletable[i] = true; @@ -425,9 +427,10 @@ vacuumLeafRoot(spgBulkDeleteState *bds, Relation index, Buffer buffer) PageGetItemId(page, i)); if (lt->tupstate == SPGIST_LIVE) { + bool dead; Assert(ItemPointerIsValid(<->heapPtr)); - if (bds->callback(<->heapPtr, bds->callback_state)) + if (bds->callback(<->heapPtr, 1, &dead, bds->callback_state) > 0) { bds->stats->tuples_removed += 1; toDelete[xlrec.nDelete] = i; @@ -965,10 +968,10 @@ spgbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, } /* Dummy callback to delete no tuples during spgvacuumcleanup */ -static bool -dummy_callback(ItemPointer itemptr, void *state) +static int +dummy_callback(ItemPointer itemptrs, int nitem, bool *deletable, void *state) { - return false; + return 0; } /* diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 739a92bdcc1..fc40317728f 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -126,7 +126,8 @@ static void index_update_stats(Relation rel, static void IndexCheckExclusion(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo); -static bool validate_index_callback(ItemPointer itemptr, void *opaque); +static int validate_index_callback(ItemPointer itemptrs, int nitem, bool *deletable, + void *opaque); static bool ReindexIsCurrentlyProcessingIndex(Oid indexOid); static void SetReindexProcessing(Oid heapOid, Oid indexOid); static void ResetReindexProcessing(void); @@ -3479,15 +3480,21 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) /* * validate_index_callback - bulkdelete callback to collect the index TIDs */ -static bool -validate_index_callback(ItemPointer itemptr, void *opaque) +static int +validate_index_callback(ItemPointer itemptrs, int nitem, bool *deletable, + void *opaque) { ValidateIndexState *state = (ValidateIndexState *) opaque; - int64 encoded = itemptr_encode(itemptr); - tuplesort_putdatum(state->tuplesort, Int64GetDatum(encoded), false); - state->itups += 1; - return false; /* never actually delete anything */ + for (int i = 0; i < nitem; i++) + { + int64 encoded = itemptr_encode(&(itemptrs[i])); + + tuplesort_putdatum(state->tuplesort, Int64GetDatum(encoded), false); + } + state->itups += nitem; + + return 0; /* never actually delete anything */ } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 33a33bf6b1c..cc8d458ea89 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -127,7 +127,7 @@ static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params, BufferAccessStrategy bstrategy); static double compute_parallel_delay(void); static VacOptValue get_vacoptval_from_boolean(DefElem *def); -static bool vac_tid_reaped(ItemPointer itemptr, void *state); +static int vac_tid_reaped(ItemPointer itemptrs, int nitem, bool *deletable, void *state); /* * GUC check function to ensure GUC value specified is within the allowable @@ -2654,10 +2654,10 @@ vac_cleanup_one_index(IndexVacuumInfo *ivinfo, IndexBulkDeleteResult *istat) * * This has the right signature to be an IndexBulkDeleteCallback. */ -static bool -vac_tid_reaped(ItemPointer itemptr, void *state) +static int +vac_tid_reaped(ItemPointer itemptrs, int nitem, bool *deletable, void *state) { TidStore *dead_items = (TidStore *) state; - return TidStoreIsMember(dead_items, itemptr); + return TidStoreIsMemberMulti(dead_items, itemptrs, nitem, deletable); } diff --git a/src/include/access/genam.h b/src/include/access/genam.h index 5b2ab181b5f..6f559a1209d 100644 --- a/src/include/access/genam.h +++ b/src/include/access/genam.h @@ -107,7 +107,8 @@ typedef struct IndexBulkDeleteResult } IndexBulkDeleteResult; /* Typedef for callback function to determine if a tuple is bulk-deletable */ -typedef bool (*IndexBulkDeleteCallback) (ItemPointer itemptr, void *state); +typedef int (*IndexBulkDeleteCallback) (ItemPointer itemptr, int nitem, + bool *deletable, void *state); /* struct definitions appear in relscan.h */ typedef struct IndexScanDescData *IndexScanDesc; -- 2.43.5