diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c index 3640977290a..761bff70807 100644 --- a/src/backend/access/gin/ginfast.c +++ b/src/backend/access/gin/ginfast.c @@ -779,7 +779,7 @@ processPendingPage(BuildAccumulator *accum, KeyArray *ka, * If stats isn't null, we count deleted pending pages into the counts. */ void -ginInsertCleanup(GinState *ginstate, bool full_clean, +ginInsertCleanup(GinState *ginstate, bool must_empty_list, bool fill_fsm, bool forceCleanup, IndexBulkDeleteResult *stats) { @@ -810,7 +810,9 @@ ginInsertCleanup(GinState *ginstate, bool full_clean, { /* * We are called from [auto]vacuum/analyze or gin_clean_pending_list() - * and we would like to wait concurrent cleanup to finish. + * and we must wait for concurrent cleanup to finish. In particular, + * VACUUM must have the opportunity to remove any dead TIDs that are + * now in the pending list. */ LockPage(index, GIN_METAPAGE_BLKNO, ExclusiveLock); workMemory = @@ -882,11 +884,12 @@ ginInsertCleanup(GinState *ginstate, bool full_clean, /* * Are we walk through the page which as we remember was a tail when - * we start our cleanup? But if caller asks us to clean up whole - * pending list then ignore old tail, we will work until list becomes - * empty. + * we start our cleanup? But if caller asks us to fully empty the + * pending list (not just move all items that were in the list when + * blknoFinish was established) then ignore old tail and work until + * the list is fully empty. */ - if (blkno == blknoFinish && full_clean == false) + if (blkno == blknoFinish && !must_empty_list) cleanupFinish = true; /* diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c index 6665ce0fec7..f0f253e9755 100644 --- a/src/backend/access/gin/ginvacuum.c +++ b/src/backend/access/gin/ginvacuum.c @@ -603,14 +603,20 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, { /* Yes, so initialize stats to zeroes */ stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult)); - - /* - * and cleanup any pending inserts - */ - ginInsertCleanup(&gvs.ginstate, !IsAutoVacuumWorkerProcess(), - false, true, stats); } + /* + * The pending list might have already-dead TIDs that VACUUM now requires + * us to remove from the index. We must force cleanup of the pending list + * now, before vacuuming proper begins, to make sure nothing is missed. + * + * When running in an autovacuum worker, we won't necessarily _fully_ + * empty the pending list. This is still safe; concurrent inserters + * cannot insert new tuples whose TIDs VACUUM needs us to remove. + */ + ginInsertCleanup(&gvs.ginstate, !IsAutoVacuumWorkerProcess(), + false, true, stats); + /* we'll re-count the tuples each time */ stats->num_index_tuples = 0; gvs.result = stats; diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h index 670a40b4bee..53282bb8ba5 100644 --- a/src/include/access/gin_private.h +++ b/src/include/access/gin_private.h @@ -462,7 +462,7 @@ extern void ginHeapTupleFastCollect(GinState *ginstate, GinTupleCollector *collector, OffsetNumber attnum, Datum value, bool isNull, ItemPointer ht_ctid); -extern void ginInsertCleanup(GinState *ginstate, bool full_clean, +extern void ginInsertCleanup(GinState *ginstate, bool must_empty_list, bool fill_fsm, bool forceCleanup, IndexBulkDeleteResult *stats); /* ginpostinglist.c */